#lang scribble/manual @(require (for-label racket/base racket/contract racket/path racket-wiki racket-wiki/translate)) @title{racket-wiki} @author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]] @defmodule[racket-wiki] The @racketmodname[racket-wiki] module starts a small self-hosted Markdown wiki. Racket provides the HTTP API, authentication, PostgreSQL-backed user and page storage, versioning, full-text search, and the initial web setup. Markdown rendering and the editing interface run in the browser. @section{Starting the Wiki} @defproc[(start [#:data-dir data-dir path-string? "wiki-data"] [#:port port exact-positive-integer? 8080] [#:listen-ip listen-ip (or/c string? #f) "127.0.0.1"] [#:secure-cookie? secure-cookie? boolean? #f] [#:site-title site-title string? "Racket Wiki"] [#:session-seconds session-seconds exact-positive-integer? (* 12 60 60)] [#:language language string? "en"]) any] { Starts the wiki using explicit server parameters. The data directory is made absolute before the server starts. Use @racket["*"] or @racket[#f] for @racket[listen-ip] to listen on all interfaces. The default address only listens on the local machine. This procedure is the convenient entry point when starting the wiki from DrRacket or another interactive Racket session. } @defproc*[([(start-wiki) any] [(start-wiki [config any/c]) any])] { Starts the wiki using either the default configuration or the supplied wiki configuration. } @section{Initial Setup} When PostgreSQL has not been configured, the schema is unavailable, no enabled administrator exists, or one of the required browser libraries is missing, normal application requests are redirected to @tt{/setup}. The setup page itself requires no JavaScript. On a fresh installation it asks for PostgreSQL server, port, database, user, password and SSL mode. The database must already exist. After testing the connection, setup creates the racket-wiki schema, asks for the first administrator, and downloads the pinned EasyMDE, Font Awesome, DOMPurify, highlight.js, and diff2html browser files. PostgreSQL connection settings are stored in @tt{database.rktd} below the configured data directory. After setup the browser is redirected to @tt{/login}; setup does not create an authenticated browser session. @section{Page Slugs} For a normal new page, the backend derives the slug from the title when the page is first saved. A later title change leaves the slug unchanged so existing links remain stable. When an editor or administrator follows a wiki-page link to a slug that does not yet exist, the browser opens an empty create view for that requested slug. A reader receives a normal not-found view. Unauthenticated browser requests are redirected to @tt{/login} before the wiki application is served. @section{Page namespaces} Pages have an explicit namespace in addition to their stable slug. Existing pages are migrated to the root namespace. A compact page reference uses @tt{namespace:slug}, for example @tt{racket:roadmap}. Explicit Markdown links may therefore use @tt{[Roadmap](racket:roadmap)}. A classic WikiWord can also be qualified, for example @tt{RWS:ModelTreeWalker}. The namespace and slug are stored separately in PostgreSQL and their combination is unique. Todo items and bookmarks are grouped by namespace in the browser. @section{Page contents} The application sidebar gives the current page's headings priority over the global page list. The contents list follows Markdown headings and is updated while a page is edited. @section{Editor appearance} EasyMDE and the rendered wiki page use matching body and heading sizes. The source editor keeps Markdown syntax visible, but heading text uses the same scale as the rendered page. Toolbar icons are provided by a locally installed Font Awesome copy. @section{Page navigation and metadata} The sidebar gives priority to a table of contents derived from the headings in the current Markdown document. The page list is secondary. The editor updates the table of contents while the document is being edited. A breadcrumb is displayed above the document. The footer displays creation and modification timestamps, the current version, and page tags. Tags are stored in the page metadata and version metadata. @section{Full-text search} Current pages are indexed by PostgreSQL using a weighted @tt{tsvector}; title terms have a higher weight than Markdown body terms. A GIN index accelerates searches. The wiki uses PostgreSQL's @tt{simple} text-search configuration so technical Dutch, English and identifier-like terms are not subjected to a language-specific stemmer. @section{Database schema migrations} Racket Wiki records PostgreSQL schema migrations in @tt{wiki_schema}. The current page is stored in @tt{pages}; every saved historical revision is stored in @tt{page_versions}. Attachments, including their binary content, are stored in @tt{attachments}. Schema migrations run in order when the application starts. Schema 7 adds page namespaces and a unique namespace/slug index. @section{Todo items} Wiki-wide todo items are written as @tt{todo(text)} in Markdown. Markers inside fenced code blocks are ignored. Current todo markers are indexed in PostgreSQL and collected in the Todo view. Ordinary Markdown task lists remain available for page-local checklists. @section{Translations} @defmodule[racket-wiki/translate] @defproc[(tr [config any/c] [key symbol?]) string?] { Returns the effective UI translation for @racket[key]. Built-in English and Dutch translations are available before the database is configured. Once the database is available, assignments in the special @tt{wiki-translations} page override the built-in strings. A line can contain several languages, for example @tt{save = nl:Opslaan, en:Save, de:Speichern}. } The UI language is selected with @tt{--language} or @racket[#:language]. The frontend obtains the same effective table from the server, so server and browser labels use one translation source. @section{Connectivity} The browser calls @tt{/api/ping} every 15 seconds. Failed or timed-out requests show an Offline indicator. A recovered connection briefly shows Online.