Since: Nocterra 0.99.1
Nocterra is a CMS that primarily serves pages. Blog articles are an optional capability that you can enable to publish dated content, automatically generate indexes (such as by year or category), and use features such as per-article Definitions and automatic Tables of Contents.
This part of the manual describes specific blog-article features and authoring tools. Most readers will want to jump directly to one of these pages:
A blog article in Nocterra is a file-based content item with metadata (author, published date, category, tags, title, description, and optional feature settings) and one or more language-specific bodies. Unlike systems that are “blog-first”, Nocterra treats articles as one content type alongside pages, so you can use articles in a traditional blog layout, or as a structured catalog of content within deeper site paths.
In a typical setup, articles:
Articles are discovered by scanning one or more configured source directories. Discovery is recursive: articles may be placed directly in the configured directory, or in any nested subdirectory structure (for example, grouping by author, topic, year, or project).
Advanced setups may:
$blog arrays (multiple independent article collections on one site),Article text and configured article templates can contain placeholders written as %name%. Common examples include %title%, %author%, %category%, %tags%, and date placeholders such as %published%.
Textual placeholders support an optional case prefix immediately after the opening percent sign:
| Prefix | Effect |
|---|---|
_ | lowercase the first character (lcfirst) |
$ | uppercase the first character (ucfirst) |
- | convert the complete value to lowercase (strtolower) |
+ | convert the complete value to uppercase (strtoupper) |
For example, %title% uses the value as stored, %_title% lowercases only its first character, %$title% uppercases only its first character, %-title% converts it to lowercase, and %+title% converts it to uppercase.
For backwards compatibility, an unprefixed placeholder keyword written with uppercase characters is treated as the $ form; for example %Title% behaves like %$title%. The prefix changes the rendered value, not the article field that is selected. Placeholders whose output is not textual case-sensitive data, such as %toc% and formatted date placeholders, do not gain a useful effect from a case prefix.
base and URL schemesNocterra does not treat the Blog module as one URL namespace. Articles and generated indexes may exist below several different top-level paths, and different categories or authors may use different article URL layouts.
base is a dispatch filter, not an article URL prefixbase defines the first URL path segments for which Nocterra should try the Blog module after normal page lookup has failed. It is primarily an optimization: requests below unrelated parts of the site do not need to pass through article routing and generated-index lookup.
A base value therefore does not mean “the URL where the blog lives”, and it is not automatically prepended to urlScheme, toc_link, or item links. If articles or generated indexes can occur below several top-level paths, all of those first path segments should be present in base.
$blog = array(
...
'base' => array(
'en' => array('/projects', '/contributions', '/volunteering'),
),
...
);
With this configuration, a request beginning with /projects, /contributions, or /volunteering can be passed to the Blog module when it was not resolved as a normal page. A request beginning with another path is not. The actual article and index URLs are still defined independently by urlScheme, toc_link, and item-specific link settings.
Because dispatch tests the first path segment, configure base using those top-level path segments rather than treating it as a deeper URL prefix.
urlScheme is used for an article?The general article configuration can define one or more urlScheme patterns. In addition, an individual author or category can define its own urlScheme. A tag cannot define an article urlScheme, because an article may contain multiple tags and therefore has no single tag value from which its article route can be selected.
For each article, Nocterra selects one urlScheme set in this order:
urlScheme_prioritize,urlScheme,article urlScheme as fallback.The default urlScheme_prioritize is category. An article can override that preference in its article header. This makes it possible, for example, to route most articles by category while allowing a particular article to prefer an author-specific route.
A selected urlScheme may itself contain multiple patterns. The first generated URL is the canonical article URL; the remaining generated URLs are alternate article URLs handled by the cache/routing layer. The alternate patterns belong to the same selected scheme set: Nocterra does not merge the category, author, and general scheme arrays for one article.
urlScheme patterns use URL scheme keys written between braces. These are not placeholders: they are resolved specifically for URL construction. Supported URL scheme keys are year, week, week0, month, month0, month_name, date, date0, date_ord, day, isodate, seq, title, category, and author.
A URL scheme key can deliberately resolve to a different value than a placeholder with the same name. For example, %author% in generated text renders the author's display name and may include a link, while {author} in a urlScheme resolves to the author's configured urlname, or to a URL-normalized form of the author key when no author definition is available. Likewise, {category} resolves to the configured category urlname or a URL-normalized category key, and {title} is converted to a URL value after applying the article's title prefix and postfix. URL scheme keys do not generate display markup such as links or tooltips.
URL scheme keys support the same optional case-prefix syntax as placeholders, but remain a separate mechanism with their own lookup and value generation:
| Prefix | Effect |
|---|---|
_ | lowercase the first character (lcfirst) |
$ | uppercase the first character (ucfirst) |
- | convert the complete value to lowercase (strtolower) |
+ | convert the complete value to uppercase (strtoupper) |
The prefix is placed directly after the opening brace, for example {_title}, {$title}, {-title}, or {+title}. An unprefixed URL scheme key written with uppercase characters is treated as the $ form; for example {Title} behaves like {$title}.
urlMapurlMap is separate from article URL schemes. It maps an exact incoming path to another target and is checked before article routing. It is useful for explicit legacy redirects or other known aliases that should not be expressed as a general article route pattern.
The routing order is important when URLs overlap:
base, the Blog module is tried.urlMap is checked first.urlScheme routes are checked next.This means a normal page can intentionally occupy a path below a configured base. It also means an article route is tested before generated index pages inside the Blog module. Avoid accidental URL collisions and make deliberate overlaps explicit in the site design.
base and one urlSchemeA conventional photography blog can use a single dispatch path and a single article route:
$blog = array(
...
'base'] => array(
'en' => '/photography',
),
...
'article' => array(
'en' => array(
'urlScheme' => '/photography/{year}/{month0}/{title}',
),
),
...
);
An article published in September 2026 with the title “Night photography in Haarlem” can therefore resolve to a URL such as /photography/2026/09/night-photography-in-haarlem. Here base merely ensures that unresolved requests beginning with /photography are offered to the Blog module; the complete article URL comes from urlScheme.
The blog could independently expose generated indexes elsewhere below the same top-level path, for example:
$blog = array(
...
'category' => array(
'en' => array(
'toc_link' => '/photography/categories',
'toc_title' => '%category%',
'toc_item' => '%title%',
),
),
...
'tag' => array(
'en' => array(
'toc_link' => '/photography/topics',
'toc_title' => '%tag%',
'toc_item' => '%title%',
),
),
...
);
A knowledge base does not need one article URL model. Stable FAQ/reference content can use permanent title-based URLs, while time-dependent updates can include their publication date. All three groups can still belong to the same Blog article collection.
$blog = array(
...
'base' => array(
'en' => '/knowledge',
),
...
'article' => array(
'en' => array(
// Fallback when neither category nor author defines a scheme.
'urlScheme' => '/knowledge/article/{title}',
'rdfa' => TRUE,
'type' => 'Article',
),
),
...
'category' => array(
'en' => array(
'toc_link' => '/knowledge',
'toc_title' => '%category%',
'toc_item' => '%title%',
'list_title' => 'Knowledge base',
),
),
...
'categories' => array(
'FAQ' => array(
'en' => array(
'name' => 'Frequently asked questions',
'urlname' => 'faq',
'link' => '/knowledge/faq',
'urlScheme' => '/knowledge/{category}/{title}',
'type' => 'WebPage',
'article_title' => '%title%',
'toc_title' => 'Frequently asked questions',
'toc_item' => '%title%',
)
),
'Reference' => array(
'en' => array(
'name' => 'Reference',
'urlname' => 'reference',
'link' => '/knowledge/reference',
'urlScheme' => '/knowledge/{category}/{title}',
'type' => 'WebPage',
'article_title' => '%title%',
'toc_title' => 'Reference',
'toc_item' => '%title%',
)
),
'Updates' => array(
'en' => array(
'name' => 'Updates',
'urlname' => 'updates',
'link' => '/knowledge/updates',
'urlScheme' => '/knowledge/{category}/{year}/{month0}/{title}',
'type' => 'NewsArticle',
'article_title' => '%title%',
'toc_title' => 'Updates',
'toc_item' => '%published% — %title%',
),
),
),
...
);
In this configuration, {category} resolves through each category's urlname. The two stable categories therefore produce paths below /knowledge/faq/ and /knowledge/reference/, even though their configured scheme template is the same. Updates resolves {category} to updates and adds the publication year and month. The general /knowledge/article/{title} scheme remains available as a fallback for categories or authors without their own scheme.
An author can use the same mechanism. For example, articles by a documentation team could receive an author-specific route when author routing is preferred:
$blog = array(
...
'authors' => array(
'Documentation Team' => array(
'en' => array(
'name' => 'Documentation Team',
'urlname' => 'docs',
'link' => '/knowledge/authors/docs',
'urlScheme' => '/knowledge/{author}/{title}',
'type' => 'Organization',
),
),
),
...
);
Whether the author or category scheme wins when both exist is controlled by urlScheme_prioritize. Tags deliberately do not participate in this article-route selection.
The following is one complete, coherent Blog configuration for a bilingual photography site. It is not an exhaustive list of every optional Blog setting; it shows how the main configuration levels fit together in one practical setup. The site is assumed to have en and ja configured as languages.
The example deliberately uses two base paths per language. Portfolio articles live below one path, while dated journal articles live below another. It also defines the general article defaults, generated indexes, two categories, five tags, and one author.
$blog = array(
'source' => '/data-articles/',
'base' => array(
'en' => array('/photography', '/journal'),
'ja' => array('/shashin', '/nikki'),
),
'addTOCToMenu' => FALSE,
'urlScheme_prioritize' => 'category',
'year' => array(
'en' => array(
'toc_link' => '/journal/archive',
'toc_name' => 'Archive',
'toc_title' => 'Photography in %year%',
'toc_item' => '%published% - %title%',
),
'ja' => array(
'toc_link' => '/nikki/archive',
'toc_name' => 'アーカイブ',
'toc_title' => '%year%年の写真',
'toc_item' => '%published% - %title%',
),
),
'author' => array(
'en' => array(
'toc_link' => '/photography/authors',
'toc_name' => 'Photographers',
'toc_title' => 'Photography by %author%',
'toc_item' => '%published% - %category% - %title%',
),
'ja' => array(
'toc_link' => '/shashin/authors',
'toc_name' => '撮影者',
'toc_title' => '%author%の写真',
'toc_item' => '%published% - %category% - %title%',
),
),
'category' => array(
'en' => array(
'toc_link' => '/photography/categories',
'toc_name' => 'Categories',
'toc_title' => '%category%',
'toc_item' => '%published% - %title%',
),
'ja' => array(
'toc_link' => '/shashin/categories',
'toc_name' => 'カテゴリー',
'toc_title' => '%category%',
'toc_item' => '%published% - %title%',
),
),
'tag' => array(
'en' => array(
'toc_link' => '/photography/tags',
'toc_name' => 'Tags',
'toc_title' => 'Articles tagged %tag%',
'toc_item' => '%published% - %category% - %title%',
),
'ja' => array(
'toc_link' => '/shashin/tags',
'toc_name' => 'タグ',
'toc_title' => '%tag%の記事',
'toc_item' => '%published% - %category% - %title%',
),
),
'article' => array(
'en' => array(
'urlScheme' => '/journal/{year}/{month0}-{date0}-{-title}',
'rdfa' => TRUE,
'type' => 'BlogPosting',
'article_title' => '%title%',
'article_subtitle' => 'By %author% - %published% - %category%',
'article_footer' => 'Tags: %Tags%',
'article_toc_title' => 'In this article',
),
'ja' => array(
'urlScheme' => '/nikki/{year}/{month0}-{date0}-{seq}',
'rdfa' => TRUE,
'type' => 'BlogPosting',
'article_title' => '%title%',
'article_subtitle' => '撮影者: %author% - %published% - %category%',
'article_footer' => 'タグ: %Tags%',
'article_toc_title' => 'この記事の内容',
),
),
'categories' => array(
'Portfolio' => array(
'en' => array(
'urlScheme' => '/photography/{category}/{-title}',
'rdfa' => TRUE,
'type' => 'Article',
'name' => 'Portfolio',
'urlname' => 'portfolio',
'link' => '/photography/portfolio',
'article_subtitle' => 'Photograph by %author%',
),
'ja' => array(
'urlScheme' => '/shashin/{category}/{seq}',
'rdfa' => TRUE,
'type' => 'Article',
'name' => '作品',
'urlname' => 'sakuhin',
'link' => '/shashin/sakuhin',
'article_subtitle' => '撮影: %author%',
),
),
'Journal' => array(
'en' => array(
'urlScheme' => '/journal/{year}/{month0}-{date0}-{-title}',
'type' => 'BlogPosting',
'name' => 'Journal',
'urlname' => 'journal',
'link' => '/journal',
),
'ja' => array(
'urlScheme' => '/nikki/{year}/{month0}-{date0}-{seq}',
'type' => 'BlogPosting',
'name' => '撮影日記',
'urlname' => 'nikki',
'link' => '/nikki',
),
),
),
'tags' => array(
'night' => array(
'en' => array('name' => 'Night photography', 'link' => '/photography/tags/night'),
'ja' => array('name' => '夜景', 'link' => '/shashin/tags/yakei'),
),
'street' => array(
'en' => array('name' => 'Street photography', 'link' => '/photography/tags/street'),
'ja' => array('name' => 'ストリート写真', 'link' => '/shashin/tags/street'),
),
'architecture' => array(
'en' => array('name' => 'Architecture', 'link' => '/photography/tags/architecture'),
'ja' => array('name' => '建築', 'link' => '/shashin/tags/kenchiku'),
),
'film' => array(
'en' => array('name' => 'Film photography', 'link' => '/photography/tags/film'),
'ja' => array('name' => 'フィルム写真', 'link' => '/shashin/tags/film'),
),
'tokyo' => array(
'en' => array('name' => 'Tokyo', 'link' => '/photography/tags/tokyo'),
'ja' => array('name' => '東京', 'link' => '/shashin/tags/tokyo'),
),
),
'authors' => array(
'aiko-tanaka' => array(
'en' => array(
'type' => 'Person',
'name' => 'Aiko Tanaka',
'urlname' => 'aiko-tanaka',
'link' => '/photography/authors/aiko-tanaka',
'urlScheme' => '/photography/by/{author}/{-title}',
),
'ja' => array(
'type' => 'Person',
'name' => '田中 愛子',
'urlname' => 'aiko-tanaka',
'link' => '/shashin/authors/aiko-tanaka',
'urlScheme' => '/shashin/by/{author}/{seq}',
),
),
),
);
The English and Japanese URL schemes do not need to be identical. In this example the English routes use the article title, while the Japanese routes use {seq} for the final identifier. This keeps the example independent of any automatic romanization of Japanese titles: the URL structure is configured explicitly for each language.
This example also shows the semantic separation between the article fields. The author key identifies one configured author. The category selects one primary category and may change both routing and rendering. Tags are a list of configured concepts that can each have their own names and links. Keywords remain free-form, language-specific metadata and can therefore be more detailed, search-oriented, or phrased differently in each language. These fields are related when article metadata is generated, but they are not interchangeable.
The following minimal article uses the configuration above. Its body is intentionally almost empty so that the semantic roles in the header remain visible:
author = aiko-tanaka
published = 2026-09-19
category = Journal
tags = night, street, tokyo
keywords:en = Shinjuku night photography, handheld low-light photography, neon reflections, Tokyo streets
keywords:ja = 新宿 夜景写真, 手持ち夜景撮影, ネオンの反射, 東京 ストリート写真
title:en = Neon after the rain
title:ja = 雨上がりのネオン
description:en = A short night-photography entry from Shinjuku after rainfall.
description:ja = 雨上がりの新宿で撮影した短い夜景写真の記事です。
[body:en]
<P>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</P>
[body:ja]
<P>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</P>
Here aiko-tanaka is the author configuration key, Journal is the single category key, and night, street, and tokyo are tag keys. The two keywords fields are independent language-specific text and do not need to correspond one-for-one with the configured tags. For example, “neon reflections” is useful article metadata without requiring a neon-reflections tag definition.
The Blog configuration has two related levels for years, authors, categories, and tags:
year, author, category, tag) defines defaults and the generated list/index for that kind of item,years, authors, categories, tags) defines individual items and can override many of those defaults.Tags and keywords are different mechanisms in Nocterra. An article has one category, one author, and zero or more configured tags. Tags refer to entries in the Blog tags configuration and can therefore have their own display name, link, urlname, and generated indexes. keywords, by contrast, are free-form, language-specific article metadata. They are not tag definitions and do not create tag indexes.
Nocterra does not use the term slug and has no Blog setting named slug. Configuration uses explicit keys and explicit properties instead. The array key identifies an author, category, or tag; name is its display name; urlname is the URL-oriented value used where that mechanism supports it; and link is an explicit destination/index URL. Keeping these concepts separate avoids treating a display name, configuration key, and URL value as if they were one implicit identifier.
The singular year, author, category, and tag definitions can configure:
| Setting | Purpose |
|---|---|
toc_link | URL of the generated top-level index for this type. It is also used as the base for relative per-item link values. |
toc_name | Name used when the generated index is added to navigation. |
toc_title | Default title/template for an individual item's generated article index. |
toc_item | Default template for an article entry in an individual item's index. |
year_toc_item, author_toc_item, category_toc_item, tag_toc_item | Context-specific alternatives to toc_item. |
toc_prologue, toc_interlude, toc_epilogue | Content placed around the generated item index. |
toc_titleLevel | Heading level used for the generated item index. |
toc_orderby | Ordering expression for articles in the generated item index. |
list_title, list_item, list_prologue, list_interlude, list_epilogue, list_titleLevel | Control the top-level generated list of all years/authors/categories/tags. |
Individual items inherit index-formatting defaults from their singular configuration and can override them. The following settings are especially important:
| Setting | Author | Category | Tag | Purpose |
|---|---|---|---|---|
name | yes | yes | yes | Display name used by generated text and indexes. |
link | yes | yes | yes | URL for that author's/category's/tag's generated index or destination. Relative values are resolved below the type's toc_link; site-absolute and external URLs are also supported. |
urlname | yes | yes | yes | URL-oriented name. For authors and categories it is also the value used by {author} or {category} in article URL schemes. |
urlScheme | yes | yes | no | Overrides article routing for articles belonging to that author or category. Tags cannot provide an article scheme because an article may contain multiple tags. |
toc_title, toc_item, context-specific *_toc_item, toc_prologue, toc_interlude, toc_epilogue, toc_titleLevel, toc_orderby | yes | yes | yes | Override the generated index presentation for this specific item. |
type | yes | yes | no | For an author, the RDFa type of the author (default Person). For a category, the semantic type used for articles in that category. |
rdfa | no | yes | no | Controls RDFa generation for articles in this category. |
article_title, article_subtitle, article_footer | no | yes | no | Category-specific article rendering templates. |
article_toc_location, article_toc_minLevel, article_toc_maxLevel, article_toc_subordinate, article_toc_quote, article_toc_tail, article_toc_trailing, article_toc_stop, article_toc_title, article_toc_titleLevel | no | yes | no | Category-specific defaults for the automatic article TOC. |
page, at | yes | no | no | Additional author metadata: a page URL and, where applicable, the organization/employer relation used in RDFa author metadata. |
This distinction is important: categories are not merely labels for grouping articles. A category can define both where its articles are routed and how those articles are rendered, including their semantic type, RDFa behavior, title/footer templates, and automatic TOC defaults. Authors can influence article routing and author metadata, while tags provide classification and generated indexes but do not control article routing or article rendering.
Generated indexes are optional. A manually maintained page may provide navigation for the same concepts without enabling a generated index. Such a page does not need to appear in the Blog configuration unless its URL also needs Blog routing behind it.
Nocterra uses multiple caches to keep content generation efficient:
Articles are rendered by combining metadata, language-specific bodies, and configured features (such as placeholder expansion and optional per-article features). General storage and caching concepts are described in the manual section at Storage engine.
Because articles are optional and URL-driven, they can be used in multiple ways. A few common examples are:
/articles/<date>/<title>), with enabled year/category/tag indexes./projects/software/<title>), where category and tag indexes become browsable “type” and “topic” listings, and features like automatic TOCs help structure long catalog entries./care/treatments/<title>), where authors map to practitioners, categories map to treatment types (e.g., CBT, EMDR, group programs), and tags map to symptoms/topics (e.g., anxiety, sleep, trauma), so visitors can browse by practitioner, by type, or by concern while optional features like automatic TOCs keep longer treatment pages easy to navigate and maintain.