Skip to content
View in the app

A better way to browse. Learn more.

ernestdefoe.online

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.
ernestdefoe.online

Extensions, themes & support for Flarum and Invision Community

Vibe coding for the community web. Report a bug, request a feature, or dig into the source — this is where the tools you use get built, in the open.

We do custom Bespoke Invision Community apps. If you have an idea for something you want then use the contact form to get in touch with us.
Knowledge base

Things that cost me a day, so they cost you none

Working notes from building Invision Community and Flarum applications. Mostly the failures that give no error at all — the ones where everything installs cleanly and quietly does the wrong thing.

92 articles

Invision Community 5

86 articles

Extensions and contracts

39

What each extension point is for, what it must declare, and what happens when it is wrong — which is usually nothing visible.

Languages and text

5

The string table, translation, and the places where text does not appear where you expected it to.

Theming, templates and forms

9

Theme hooks, CSS that survives both colour schemes, and building forms that do not throw on render.

Background work and scheduled tasks

5

The queue system, work that has to happen after the response, and jobs that finish without doing anything.

Data, settings and storage

11

The database layer, settings, tags, file storage, and backing up a live site.

AI features and expectations

5

What these features do, what they cost, and what buyers reasonably but wrongly assume they do.

Application structure and releases

11

The JSON files an application is made of, versioning and upgrade steps, and testing from the command line.

Realtime, chat and calls

1

WebSocket gateways, relays and the server-side pieces live features depend on — where "it works when I test it" and "it works for your members" are different claims.

Nothing matches that.

Pages page content is not a template: why template syntax prints itself, and why removing an element does not stick

Two things about customising a Pages "html" page that each cost an hour, and both fail in a way that looks like something else.

1. page_content is not run through the template engine

A theme's custom header is compiled — Theme::getHeaderAndFooter() calls Theme::compileTemplate(), so you can write {{if}}, {{foreach}} and {expression="..."} in it and they execute.

A Pages page's page_content is not. Pages parses its own tags — {database="kb"}, {block="..."}, {media="..."} — and emits everything else verbatim. Put template logic in a page and visitors see this at the top of the screen:

{{$edKbPath = rtrim( (string) parse_url( $_SERVER['REQUEST_URI'] ?? '', PHP_URL_PATH ), '/' );}}
{{$edKbIndex = ( $edKbPath === '/kb' or $edKbPath === '' );}}
{{if !$edKbIndex}}

The nasty part: it does not fail consistently across routes. A page with a database attached serves the listing, each category, each record and the submit form from the same page_content. In testing, the listing route swallowed the logic and rendered correctly, while the record and submit routes printed the raw source. Checking the index, seeing clean output, and assuming the rest matched was enough to ship it.

If one page serves several routes, check every route for leaked syntax, not just the one you were looking at. HTTP 200 on the others proves nothing.

What to do instead

Generate the HTML somewhere that can run code and write the result in as static markup — a small script that reads the database and updates cms_pages.page_content is enough, and it can be re-run whenever the content changes. For anything genuinely dynamic, use a Pages block (which is compiled) or a theme template. Keep logic out of the page body entirely.

A plain <script> in the page body is fine, incidentally — scripts run regardless of what the parser did or did not interpret. That is a reasonable place for behaviour that has to differ by route.

2. Removing an element from the DOM does not stick

Having replaced a listing with your own index, the obvious next step is to delete the stock one:

var listing = document.querySelector('.ipsData--cms-category-articles');
listing.closest('.ipsBox').remove();   // comes straight back

It disappears and then returns. Invision Community 5 wraps content listings in an <i-data> custom element, and custom elements hydrate after DOMContentLoaded and rebuild their contents. Anything you removed in the meantime is re-created, with no error and no clue as to why.

Hide it with CSS instead — a stylesheet rule cannot be undone by a re-render:

/* inline, before paint, so the old listing never flashes */
<script>
if ( /\/kb$/.test( location.pathname.replace(/\/+$/, '') ) ) {
    document.documentElement.classList.add('kb-index');
}
</script>

html.kb-index .ipsBox--cmsCategoryArticles { display: none !important; }

Setting the class on <html> from an inline script — not a deferred one — means the rule applies before first paint, so there is no flash of the thing you are suppressing.

3. And while you are here: inline scripts run before the rest of the page exists

Page content is emitted before the record markup further down the document. A script in it that looks for the article body finds nothing, and if the same script also touches something above it, half your code appears to work:

if ( document.readyState === 'loading' ) {
    document.addEventListener( 'DOMContentLoaded', start );
} else {
    start();
}

Which is also why the class in the previous section is set immediately rather than inside start() — one job needs to happen before paint, the other needs the document to exist.


User Feedback

Recommended Comments

There are no comments to display.

Account

Navigation

Search

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.