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.

What you can and cannot test from the command line

Loading init.php from a CLI script gives you most of the framework, and it is by far the fastest way to test an application. Some things genuinely cannot run there, and knowing which saves a lot of time spent debugging the harness instead of the code.

The basic harness

<?php
require_once '/var/www/html/init.php';

use IPS\Db;

/* IPS installs its own error handlers. Under CLI a fatal can end the process
   with NO OUTPUT AT ALL, which reads as the script silently stopping. Write to
   a file so failures are visible. */
const LOG = '/tmp/mytest.log';
@unlink( LOG );

function say( string $line = '' ): void
{
    file_put_contents( LOG, $line . PHP_EOL, FILE_APPEND );
}

set_exception_handler( function ( $e ) {
    say( 'FATAL ' . get_class( $e ) . ': ' . $e->getMessage() );
    say( $e->getTraceAsString() );
} );

That error-handler detail is the first thing to get right. Without it, a fatal produces an empty terminal and no clue.

What does not work, and why

ThingWhat happens
ACP or front controllersRuntimeException: Only subclasses of Dispatcher can be instantiated
Widget::configuration()Reaches for a Dispatcher, same error
Anything building a URL from the requestUndefined array key "SERVER_NAME", then QUERY_STRING, then more
S3 / R2 file storageBAD_XML unless HTTPS=on is in the environment
Anything reading the logged-in memberWorks, but the member is a guest unless you load one explicitly

Do not chase the $_SERVER errors

Filling in SERVER_NAME gets you as far as QUERY_STRING, then SCRIPT_NAME, and eventually to the Dispatcher error anyway. If a test needs a controller, that test needs a browser. Test the piece the controller calls instead:

// Instead of building the whole settings form, check the element it adds
$field = new \IPS\Helpers\Form\Sort( 'myapp_order', $order, FALSE, array( 'checkboxes' => 'myapp_' ) );

Test widgets through __toString(), not render()

__toString() runs init(), _render() and the caching layer — the whole path the theme actually uses. Calling render() directly bypasses all of it and proves considerably less.

Reproduce the consumer, not your own method

The most valuable CLI tests are the ones that imitate what core does with your code, because that is where extension-point mistakes surface. Calling your own method and getting a sensible answer back proves very little.

/* This is the test that catches a broken core/Loader extension: it reproduces
   the dispatcher's merge loop across every installed application. */
foreach ( Application::allExtensions( 'core', 'Loader' ) as $key => $loader )
{
    foreach ( $loader->js() as $js )
    {
        if ( !is_array( $js ) )
        {
            say( 'BREAK ' . $key . ' returns a flat list and will 500 every page' );
        }
    }
}

The same idea covers language keys an ACP screen will demand, extension classes that must exist, and any contract core enforces at runtime rather than at install.

Static checks catch what CLI cannot run

Where the failure is inside a controller, read the source instead of executing it. Scanning for 'app' => ..., 'key' => ... pairs on every Form\Editor and confirming the named EditorLocations file exists takes a second and catches a bug that otherwise only appears when a human opens the page.

Verified against

Invision Community 5.0.19, by building and testing applications against it.


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.