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.

Where OAuth account links live, and why you should not keep your own copy

If your application connects members to an outside service — Discord, Telegram, anything with OAuth — the temptation is to add a table: myapp_links, member id, remote id, done. Do not. Invision Community already stores that relationship, and keeping a second copy creates a class of bug you cannot fix later.

core_login_links

When a member signs in with, or connects, an OAuth login handler, the link is recorded in core_login_links:

  • token_login_method — the id of the login handler
  • token_member — the member
  • token_identifier — their id on the remote service
  • token_linked — whether the link is currently active
  • token_access_token — the OAuth token, which you will need if you intend to act on the member's behalf

Read it through one accessor and use that everywhere:

public static function idFor( Member $member ): ?string
{
    if ( !$member->member_id )
    {
        return NULL;
    }

    try
    {
        $handler = static::instance();

        return (string) Db::i()->select(
            'token_identifier',
            'core_login_links',
            array( 'token_login_method=? AND token_member=? AND token_linked=1',
                   $handler->id, $member->member_id )
        )->first() ?: NULL;
    }
    catch ( \Throwable $e )
    {
        return NULL;
    }
}

Why a second copy goes wrong

A member who disconnects the service in Account Settings is unlinked in core_login_links and nowhere else. Your table still says they are connected, so your application keeps acting on an account the member deliberately disconnected — still assigning them roles, still treating them as verified. There is no event you can subscribe to that reliably repairs this, and the member has already done the one thing the interface offered them.

The same applies in reverse to merges, deletions and re-links. Every one of them is handled for you if you have no second copy to keep in step.

Requesting the scopes you will actually need

Scopes are granted at authorisation time and cannot be widened later using a token that was issued without them. If your application intends to act on the member's behalf, ask up front:

protected function scopesToRequest( array $additional = NULL ): array
{
    return array( 'identify', 'email', 'guilds.join' );
}

Discovering afterwards that every existing member must re-authorise is an expensive mistake, because most of them never will.

Two things worth checking

Do not trust an unverified email. Services will hand over an address the account holder has never proved they own. Accepting it lets somebody register on the remote service against another person's address and land on their forum account:

protected function authenticatedEmail( string $accessToken ): ?string
{
    $data = $this->_userData( $accessToken );

    if ( empty( $data['verified'] ) )
    {
        return NULL;
    }

    return $data['email'] ?: NULL;
}

Set $allowMultiple = FALSE on the handler extension if only one connection per community makes sense. Two configured handlers means two sets of credentials and two sets of links, and every lookup that assumes one answer starts returning whichever it found first.

The migration payoff

There is a further reason to use the suite's table rather than your own. If somebody ever moves to your application from a competing one, the expensive thing to carry across is not settings — those take minutes to re-enter — it is the linked accounts. Every member who has to reconnect is a member who mostly will not, and the community quietly loses the feature it was paying for. Writing rows into core_login_links is a well-defined migration target. Writing them into a private table nobody else knows about is not.



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.