myWebLog
Themes
A “theme” refers to the templates, styles, and other assets used to render content.
Templates
myWebLog uses DotLiquid, a .NET implementation of the Liquid template language, to render pages and posts. A complete theme contains four templates:
layout.liquid
is the overall page layout. The value{{ content }}
is replaced with the actual page content.index.liquid
renders lists of posts, including the category and tag post lists.single-page.liquid
renders individual pages.single-post.liquid
renders individual posts.
Themes can also support different templates for pages and posts. A practical example is the theme for the site you're reading now; project-page.liquid
is the template for pages associated with an open source project; solution-page.liquid
is used to generate the pages on the homepage sidebar's “About” links; and, speaking of the home page, home-page.liquid
is used render it.
htmx Support
htmx brings the interactivity of the SPA to basic HTML content, allowing us to swap out portions of the page instead of the entire page. This has several benefits, the biggest one being speed; none of the page-wide styles, scripts, and images need to be changed when some other content is swapped out. The admin area of myWebLog uses htmx extensively; the inline editing of tag mappings and categories, and every page navigation, do not reload the entire page.
For a publicly-facing weblog, though, using htmx will be relatively easy. Placing hx-boost="true"
on an element makes all the non-external links contained in that element be handled by htmx, and by default, the content it receives will replace the inner HTML of that element. When rendering content for a link going through htmx, myWebLog uses the layout-partial.liquid
template. If the hx-boost
attribute is applied to the <body>
tag, the partial layout should render the entire page, minus the <head>
content (except for the <title>
); if it is placed on a <main>
tag, the layout should render whatever content belongs there. (See the include_template
custom Liquid filter below for a strategy to reduce duplication between layout
and layout-partial
.)
The actual decision of where to place the hx-boost
attribute is dependent on what you want to preserve between navigations. Placing it on the <body>
tag lets you update things like highlighting the active page, while placing it on a content <div>
(or other tag) will only replace that content.
Theme Files
Any theme files (*-theme.zip
) that are in the same directory as myWebLog are loaded automatically when it starts. The basic installation comes with a default theme (as well as an admin theme, which should not be modified, and is not able to be modified via the application). Within a theme file, any files in its wwwroot
directory are loaded as theme assets; these are described below.
Themes can be loaded in three ways:
- Through the application, under the “Admin” menu
- Via the CLI
load-theme
command - By placing the
*-theme.zip
file in the application directory, then restarting
Both the first and last options above result in the theme's .zip
file being in the application's directory, so they will be loaded each time the application starts. While this is not required – the theme information in the database should persist between restarts – this enables upgrades to provided themes to occur transparently, and can be utilized for custom themes as well. The admin page will show “NOT ON DISK” for themes where the .zip
file is not in the directory.
A few other notes:
- The active theme for each web log is part of its backup and restore process
- If a theme is loaded via the CLI while the application is running, you can refresh the theme cache to make it available without restarting the application. (Themes loaded through the application are immediately available.)
Theme Assets
In a theme .zip
file, items within the wwwroot
folder are considered theme assets. They are stored in the database, and as such, are included in database backups. Assets are served with a Last-Modified
header and a “cache this for 30 days” header, and support requests with an If-Modified-Since
header (in short, clients can check to see if their cached version is still good, and not download them again, saving you bandwidth). For this reason, though, if you change an asset, you may need to do a hard refresh before you see your changes.
There are a few assets with special handling:
- Links to
style.css
andfavicon.ico
will be added to the<head>
via thepage_head
custom Liquid tag if these assets exist. script.js
will be added to the bottom of the<body>
via thepage_foot
custom Liquid tag if that asset exists.
For others, you can use the theme_asset
custom Liquid filter to generate a link for the asset; see its description below.
Displaying Content in Templates
The rendering context for pages can vary based on what is being rendered; however, the following values are always available. (The true/false values will be false if they are not set.)
page_title
contains the title of the page. For post lists, this will contain the page number of the list; for category and tag lists, this will also contain the name of the category or the tag. For single pages and posts, this will is the title of the page or post.web_log
contains the blog settings.page_list
is the list of pages with “Show in Page List” checked.categories
contains a list of all categories defined for the blog.current_page
is the URL of the current pagemessages
contains messages from the server (for public blogs, the only message ever sent is a log off success message).is_home
will be true if the page being rendered is the home page for the blog, whether it is a specific page or a list of the most recent posts.is_page
will be true if the content being displayed is a page.is_post
will be true if the content being displayed is a post.is_category
will be true if the page being rendered is a category archive page.is_tag
will be true if the page being rendered is a tag archive page.is_logged_on
will be true if there is a user logged on.is_author
will be true if the current user has Author privileges (or above).is_editor
will be true if the current user has Editor privileges (or above).is_web_log_admin
will be true if the current user has Web Log Admin privileges (or above).is_administrator
will be true if the current user has Administrator privileges.generator
is the string that the{% page_head %}
tag and feed generator use as the “generator” for the given page or feed.htmx_script
is ascript
tag that loads htmx from the unpkg.com content delivery network. (It is automatically applied via{% page_foot %}
if the web log's “Auto-Load htmx” option is set.)
Custom Filters and Tags
myWebLog provides several custom filters and tags used to assist with common processes a theme may need to perform. These are detailed below.
Page Header and Footer
{% page_head %}
should go within the <head>
tag of any theme's layout, as it does much of the heavy lifting for your page.
- It adds links to
style.css
andfavicon.ico
, if those exist in your theme layout. - It adds RSS feed links to the home page and category / tag pages (if those feeds are not disabled).
- It adds a canonical link for pages and posts.
- It adds a generator meta tag to identify that the page was served with myWebLog.
{% page_foot %}
can be placed anywhere in your page (including the <head>
, if you really wanted to), but will generally work best just inside the closing <body>
tag. It adds script references for htmx (if enabled) and script.js
, if it exists in your theme.
Links
absolute_link
generates an absolute URL for the given link. It can take a page, a post, or a string, and will use the web log's URL base and the permalink (for pages or posts) to create a complete link that will always work. relative_link
does the same thing, but excludes the authority (scheme, host, and port) portion of the URL base for the web log. Within the context of a link that someone would click in a web page, these are equivalent; some links may need to be absolute, though, so both filters are provided.
ex. {{ "2020/my-page.html" | absolute_link }}
would result in https://yadda.example.com/2020/my-page.html
, while {{ "2020/my-page.html" | relative_link }}
would generate /2020/my-page.html
.
category_link
generates a relative URL for the given category. Using the categories
context item is the best way to get a category. Within a post, {% for cat_id in post.category_ids %}
will loop through the category IDs for the post, and the category can be obtained via {% assign cat = categories | where: "id", cat_id | first %}
. In a sidebar, you may simply iterate over the categories themselves {% for cat in categories %}
; the categories are sorted/grouped alphabetically. However you obtain the category, though, {{ cat | category_link }}
will generate a link to the first page of posts for that category (include its subcategories).
tag_link
generates a relative URL for the given tag. It uses any tag mappings that have been defined. Within a {% for tag in post.tags %}
loop, <a href="{{ tag | tag_link }}">{{ tag }}</a>
generates a link to the first page of posts tagged with the same tag.
edit_page_link
and edit_post_link
generate links to edit a particular page or post. Note that, if a user is not logged on, they will need to do so before these links will work. You can also wrap them in {% if logged_on %}...{% endif %}
to only render them if a user is already logged on. Each filter will take either its type (post or page) or a string with the ID of a post or page.
theme_asset
generates a link to an asset within a given theme. The path given there should begin with the root of the wwwroot
folder in your theme definition. For example, if there was a file wwwroot/img/profile.png
, it could be rendered as <img src="{{ "img/profile.png" | theme_asset }}">
. Note that this does not validate that the asset actually exists; if it does not, it will render as a broken image.
Others
value
takes an array of metadata items and returns the first value where the name matches. Both pages and posts have metadata items. If the metadata item does not exist, the string -- [name] not found --
is returned.
A quick example will give you an idea of where this might be useful. Let's say you are a musician, and you have albums for sale. If you create a post about each album, you can use a purchase_url
metadata item to generate a “Buy Now” link (maybe via an “album-page” template, or logic in the “single-page” template); once the album is no longer being sold, you can remove that metadata item. Here is one way this could be implemented:
{% assign purchase_url = post.metadata | value: "purchase_url" %} {% if purchase_url == "-- purchase_url not found --" %} <em>This title is not currently available</em> {% else %} <a href="{{ purchase_url }}" target="_blank" rel="noopener">Buy Now!</a> {% endif %}
As mentioned above, the theme for this site uses some custom templates; these use metadata extensively to customize the layout of the pages, while keeping the same overall site theming.
Author names are also exposed as metadata collections; in an index template, model.authors
indexes the authors' preferred name by their ID. {{ model.authors | value: post.author_id }}
will display the author's name.