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.

Extension method signatures are fixed, and a mismatch is a fatal on install

You write an extension, the file lints clean, and installing the application dies:

Fatal error: Declaration of IPS\myapp\extensions\core\FileStorage\Audio::move(?int $offset = null): void
must be compatible with IPS\Extensions\FileStorageAbstract::move(int $offset, int $storageConfiguration,
?int $oldConfiguration = null): void

Extension methods are declared by an abstract class, and PHP enforces the signature exactly. This is not a warning you can ship past — the application will not install at all, and php -l will not catch it because the file itself is syntactically fine.

Do not guess. Read the abstract, or copy a working one

The signatures are not what you would design. Two examples that catch people:

/* FileStorage - THREE arguments, and the extra two matter */
public function move( int $offset, int $storageConfiguration, int $oldConfiguration=NULL ): void

/* EditorLocations - SIX arguments, including the attachment array and a viewOnly flag */
public function attachmentPermissionCheck( Member $member, ?int $id1, ?int $id2, ?string $id3,
                                           array $attachment, bool $viewOnly=FALSE ): bool

/* and its lookup returns a union you would not have written */
public function attachmentLookup( ?int $id1=NULL, ?int $id2=NULL, ?string $id3=NULL ): Model|Content|Url|Member|null

Two reliable ways to get them right, both faster than guessing:

# the authority
grep -nE "abstract public function" system/Extensions/FileStorageAbstract.php

# a working implementation to copy the shape from
ls applications/*/extensions/core/FileStorage/*.php
sed -n '/function move/,/^\t}/p' applications/blog/extensions/core/FileStorage/Blogs.php

Redeclaring a typed property is the same trap, one level down

This one is worse, because it does not fail on install. It fails the moment the class is first loaded, which may be long after you shipped:

/* fatal: must be array (as in class IPS\Patterns\ActiveRecord) */
protected static $multitons = array();

/* correct */
protected static array $multitons = array();

An ActiveRecord subclass with the untyped version installs happily, passes a lint, builds into a tar, and dies the first time anything actually uses the model. It was found here by seeding data for a screenshot — nothing before that had touched the class.

Some extensions are required, not optional

An Editor form field needs a matching EditorLocations extension. There is no fallback: build the form without one and IPS throws. The field's app and key do not create it for you.

$form->add( new Editor( 'my_notes', NULL, FALSE, array(
    'app' => 'myapp', 'key' => 'Episodes', 'autoSaveKey' => 'myapp-episode',
) ) );

/* requires applications/myapp/extensions/core/EditorLocations/Episodes.php
   AND an entry in data/extensions.json */

Why this is worth a checklist rather than a memory

Every one of these fails in a way that points somewhere unhelpful. The FileStorage message names the abstract, which is fair enough. The $multitons one names the parent class and gives you no hint that the fix is a single word. The missing EditorLocations throws from inside the form helper, several frames from anything you wrote.

So before writing any extension: open the abstract, or open an existing implementation in applications/*/extensions/. It takes a minute and it replaces an install-time fatal with nothing at all.



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.