pragma web builds a simple blog/site from Markdown sources. It reads a site config, parses Markdown to HTML, creates index pages, a chronological "scroll," per-tag listings ("tag index"), individual post pages, and a simple RSS feed (feed.xml) of the previous 20 posts. It uses a simple templating system to allow flexible output and modularity. The goals are simplicity and speed.
- Language: C
- Libraries/Dependencies: code: C standard library. resulting website: none; built-in support for optional use of GLightbox [https://github.com/biati-digital/glightbox(https://github.com/biati-digital/glightbox)].
- Status: Active development, working beta
- License: MIT
- Loads site config (
pragma_config.yml) and site sources from a directory; sources are plaintext HTML and/or Markdown. pragma-web expects filenames ending in .txt, but it shouldn't really matter (future refinements abound). - Parses Markdown into HTML if source file does not specify
parse:no(render headings, paragraphs, inline emphasis, code, images, basic lists). - Builds:
index.html,index1.html, ... (paged, linked indices);s/index.html(chronological scroll of all content, newest to oldest, which should probably also be paged like the indices);t/index.html+/t/{tag}.html(folksonomy tag index: all tags and pages that use them); and/p/{filename}.html(one page per post, matching input filename: fido.txt => fido.html)
- Uses a basic templating system for output, with tokens such as
{TITLE},{PAGETITLE},{DATE},{TAGS},{PAGE_URL},{FORWARD},{BACK},{MAIN_IMAGE}, etc (see "Configuration" below)
Building The software depends on standard library headers and should build cleanly without special configuration:
make
or:
gcc -o pragma src/*.c
After building the executable, the first step is to create a new site, which you can do by passing the -c argument and specifying the intended path.
./pragma -c /path/to/site
You'll need to edit /path/to/site/pragma_config.yml before building the site. Optionally, you can customize the header/footer templates, output templates, JavaScript file (blank by default), and CSS.
Generating an Existing Site
./pragma -s /path/to/site [-o /path/to/output]
If -o is omitted, output defaults to the site source directory, and index.html will live in the parent directory of dat. Generally, that's fine, if not a totally clean separation of source data and output. You can just deploy the site with rsync (or cp, or anything) and omit dat, of course. -h displays basic usage/help.
By default, pragma-web generates a "production" site that points to the base URL specified in the configuration file. To test things locally, set the environment variable PRAGMA_LOCAL_BASE. For example, PRAGMA_LOCAL_BASE="http://localhost:8000/". The generated site will use that value as its base URL, allowing you to test quickly with, e.g., python3 -m http.server 8000.
If the PRAGMA_LOCAL_BASE variable is set, pragma-web requires you to confirm that you really want an alternative base URL. (TODO: allow override, but I added this as a speed bump for myself.)
- pragma_config.yml (required, generated by
pragma -c) lives in the site source directory and specifies site metadata, index size, header/footer paths, base URL, icon settings, etc. - Header/footer and output templates in the site path supply basic layout. pragma-web generates navigation links and folksonomy catalogues. The output templates are:
templates/index_item.htmlControls how pragma-web arranges output for post entries or excerpts on indicestemplates/navigation.htmlManages the appearance of the navigation widget (forward/back)tempaltes/post_card.htmlSpecify the post card (embeddable rendering); same as index_item by default, but can be changed for use in other contextstemplates/single_page.htmlSpecify the layout for a single HTML page of the site, i.e. a single post
You may want to change the default appearance of the site, which has minimal styling. pragma-web will generate HTML that includes the following CSS classes. The design is responsive by default.
Core Layout:
.main_body- Container for the main content area.post_card- Container for each post (used in index, single pages, scroll).post_head- Header section of each post containing icon and title.post_body- Body content area (for single pages)
Icon and Visual Elements:
.post_icon- Container for the post icon.icon- The actual icon image
Title and Metadata:
.post_title- Container for post title and metadata (default styled with calc width and overflow handling).post_metadata- Container for date and tags information.post_tags- Container for tag links
Content Areas:
.post_in_index- Content area for posts displayed in index pages (shows summary/truncated content).post_image_wrapper- Wrapper for images (default styled: centered, clear:both)img.post- Post images (default styled: centered, max-width 75%, responsive)
Navigation:
.post_navigation- Container for prev/next navigation.nav_prev- Previous post navigation container.nav_next- Next post navigation container.nav_label- Navigation labels ("« newer", "older »").nav_title- Titles of prev/next posts in navigation
Index and Feed Elements:
.read_more- "read more »" links in index pages.foot- Footer pagination area (default styled: centered, 15% width, larger font)
Special Content:
.gallery- Image gallery containers (from markdown!!directorysyntax).glightbox- Lightbox gallery links (used by make_gallery.py helper)
- Wide-char parsing is enabled with
setlocale(LC_CTYPE, "en_US.UTF-8");at the beginning ofmain(), and the argument tosetlocale()should be changed for other locales (or better yet, I should move it to the configuration file). - Markdown is handled in pragma_markdown.c; page assembly in pragma_page_builder.c
- Index/scroll/tag/rss builders in corresponding *_builder.c files
- Function headers document basic arguments and usage as well as memory ownership (callers free where noted)
- Time handling uses localtime(), which technically is not thread-safe but is fine in this case
- Markdown: coverage is basic (no tables, fenced blocks, etc.)
- Need to centralize error and status logging
- Separation of concerns between rednering and assembly is way better than it was in the hacked-together prototype, but still could be refined
- Performance testing on unrealistically large sites (50,000+ posts, 160k+ tags) has worked fine, but I need to optimize some expensive parts of tag index generation in particular.