Add content index: keywords, tags/categories, search, and XML sitemap
Ships Blog tags/categories, Internal search, and XML sitemap together as
one shared mechanism, plus a new meta keywords request, rather than three
separate ones - the "worth deciding together" call ISSUES.md made when
XML sitemap was first written.
Content stays in files. Per-page metadata is four Twig blocks in the
root layout, the same override mechanism already used for
title/description/og_* - keywords (rendered), tags/changefreq/priority
(not rendered, harvested only). App\ContentIndexer crawls every routable
page (Overlay::listPageDirs(), new) and pulls each block via
Renderer::renderForIndex() (new) calling Twig's own renderBlock() API,
not regex-parsing .twig source, so overrides and layout inheritance
resolve exactly like a real render. Rendered HTML is stripped and
indexed into a SQLite FTS5 table for search.
Off by default (content_index_enabled) - same posture as Matomo/admin
auth, since this is a real SQLite dependency plenty of sites won't want.
Verified true zero footprint when disabled: no data/novaconium.sqlite
gets created, all three consumer routes 404 like they don't exist.
content_index_auto (default true) reindexes lazily on first stale touch
of a consumer route, never on a normal page view; both that path and the
explicit `php novaconium/bin/index-content.php` share one reindex().
Two real bugs caught by testing, not review: a reentrancy bug where
/search's own sidecar calling ensureFresh() during the crawl triggered a
nested reindex() mid-transaction (fixed with a static re-entrancy guard),
and a wrong PDO constant in the search sidecar. Also fixed two unrelated
pre-existing bugs found while building this: an unescaped {{ }} in
admin/docs/sidecars that fataled Twig on any real render of that page,
and a stale "no database layer yet" claim there and in Lib\Input's
docblock, left over from before Lib\Db shipped.
Lib\Db's migrations_dir now accepts an ordered list of roots, not just
one path, so the content index's schema could ship as a framework
migration (novaconium/migrations/) without colliding with project
migrations in App/migrations/ - the two-root extension point flagged in
AGENTS.md when SQLite groundwork shipped. Migrations are tracked by path
relative to the repo root rather than bare filename so two roots with a
same-named file can't shadow each other.
New consumer routes: novaconium/pages/sitemap.xml/ and
novaconium/pages/search/ (framework defaults), App/pages/blog/tag/[tag]/
(project-owned, since blog/ is project content - the existing hand
-written post array in App/pages/blog/index.php is untouched). Added
tags to the 4 existing blog posts as a real demonstration.
Closes the Blog tags/categories, Internal search, and XML sitemap
backlog items in novaconium/ISSUES.md.
This commit is contained in:
+27
-4
@@ -48,9 +48,13 @@ return [
|
||||
// wholly replaced on a framework update — see
|
||||
// /admin/docs/getting-started's "Updating the framework" section) — a
|
||||
// top-level data/ directory, project-owned like App/, is the only safe
|
||||
// place for it. migrations_dir is optional per connection; omit it to
|
||||
// never run migrations against that connection (e.g. a read-only
|
||||
// legacy database). NOTE: unlike every other key here, App/config.php
|
||||
// place for it. migrations_dir is optional per connection (omit it to
|
||||
// never run migrations against that connection, e.g. a read-only
|
||||
// legacy database) and accepts either one path or an ordered list of
|
||||
// roots — the default connection lists novaconium/migrations/ (framework
|
||||
// -shipped schema, e.g. the content index — see /admin/docs/content-index)
|
||||
// before App/migrations/ (project migrations), so framework migrations
|
||||
// always apply first. NOTE: unlike every other key here, App/config.php
|
||||
// merges into db_connections one level deeper than a normal shallow
|
||||
// override — see the comment on Lib\Db::config() — so adding a second
|
||||
// connection there doesn't require repeating 'default'.
|
||||
@@ -58,7 +62,10 @@ return [
|
||||
'default' => [
|
||||
'driver' => 'sqlite',
|
||||
'path' => __DIR__ . '/../data/novaconium.sqlite',
|
||||
'migrations_dir' => __DIR__ . '/../App/migrations',
|
||||
'migrations_dir' => [
|
||||
__DIR__ . '/migrations',
|
||||
__DIR__ . '/../App/migrations',
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
@@ -73,4 +80,20 @@ return [
|
||||
// since a world-readable cached copy would otherwise permanently leak
|
||||
// the draft the first time an admin previewed it.
|
||||
'draft_routes' => [],
|
||||
|
||||
// Content index (see /admin/docs/content-index) — backs /sitemap.xml,
|
||||
// /search, and blog tag browsing. Off by default: all three depend on
|
||||
// SQLite (Lib\Db), a real dependency plenty of sites built on this
|
||||
// framework won't want at all, the same reasoning that keeps Matomo
|
||||
// and admin auth off by default above. When false, all three routes
|
||||
// 404 exactly as if they didn't exist, and nothing ever touches
|
||||
// Lib\Db because of this feature — no data/novaconium.sqlite gets
|
||||
// created just because the code exists. content_index_auto only
|
||||
// matters once enabled: true (the default) reindexes lazily,
|
||||
// on-demand, the first time a stale index is actually needed (never on
|
||||
// a normal page view); false disables that and leaves indexing
|
||||
// entirely to `php novaconium/bin/index-content.php`, e.g. from a
|
||||
// deploy step.
|
||||
'content_index_enabled' => false,
|
||||
'content_index_auto' => true,
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user