PHPackages                             ekumanov/flarum-ext-discussion-og-meta - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. ekumanov/flarum-ext-discussion-og-meta

ActiveFlarum-extension[Utility &amp; Helpers](/categories/utility)

ekumanov/flarum-ext-discussion-og-meta
======================================

Emits OpenGraph and Twitter Card meta tags on Flarum discussion pages so external link previews show the actual discussion title, excerpt, and (when available) first-post image.

v1.0.0(3w ago)04MITPHPPHP ^8.2

Since May 16Pushed 3w agoCompare

[ Source](https://github.com/ekumanov/flarum-ext-discussion-og-meta)[ Packagist](https://packagist.org/packages/ekumanov/flarum-ext-discussion-og-meta)[ RSS](/packages/ekumanov-flarum-ext-discussion-og-meta/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (1)Dependencies (1)Versions (2)Used By (0)

Discussion OpenGraph Meta for Flarum 2.0
========================================

[](#discussion-opengraph-meta-for-flarum-20)

[![License](https://camo.githubusercontent.com/7013272bd27ece47364536a221edb554cd69683b68a46fc0ee96881174c4214c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e737667)](LICENSE)[![Latest Stable Version](https://camo.githubusercontent.com/f0fd2f38d1432a3f5d244465cfbff51632892dda4e7b21fd5339fecda80c8d57/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f656b756d616e6f762f666c6172756d2d6578742d64697363757373696f6e2d6f672d6d6574612e737667)](https://packagist.org/packages/ekumanov/flarum-ext-discussion-og-meta)[![Total Downloads](https://camo.githubusercontent.com/604af8c9977d6ee0eb932bbe5d10865721f05b12af063a2a600737abe15be4e7/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f656b756d616e6f762f666c6172756d2d6578742d64697363757373696f6e2d6f672d6d6574612e737667)](https://packagist.org/packages/ekumanov/flarum-ext-discussion-og-meta)[![Backend](https://github.com/ekumanov/flarum-ext-discussion-og-meta/actions/workflows/backend.yml/badge.svg)](https://github.com/ekumanov/flarum-ext-discussion-og-meta/actions/workflows/backend.yml)

Emits per-discussion **OpenGraph** and **Twitter Card** meta tags in the `` of Flarum 2.0 discussion pages, so when someone pastes a discussion URL into Slack, Discord, Facebook, X, iMessage, Telegram, or any other unfurler, the preview shows the **actual discussion title and first-post excerpt** — not the same generic forum description every time.

Built with [Claude Code](https://claude.com/claude-code).

Why this exists
---------------

[](#why-this-exists)

Out of the box, every page on a Flarum forum carries the same single forum-level ``. So a link to `https://example.com/d/2449-pianos-and-design` previews everywhere as the forum's generic description ("Piano forum for piano, keyboard, synth, players and music enthusiasts..."), regardless of what the thread is actually about. That makes shared links look interchangeable and kills click-through.

This extension fixes that by reading the discussion + first-post data that Flarum's discussion route handler **already** loads, and turning it into a small block of per-page OpenGraph + Twitter meta tags.

What it emits
-------------

[](#what-it-emits)

On every **public** discussion page (`/d/{id}-{slug}`):

```

```

`twitter:card` switches to `summary_large_image` when the first post contains an image; otherwise it's plain `summary`. `og:image` and `twitter:image` are only emitted when an image is found.

What it does NOT do
-------------------

[](#what-it-does-not-do)

- No `INSERT`, `UPDATE`, or `DELETE` against the database. No migrations.
- No new tables, columns, or settings rows.
- No JSON-LD / structured-data emission.
- No sitemap integration (use [fof/sitemap](https://github.com/FriendsOfFlarum/sitemap) for that).
- No admin UI or settings panel — it uses your existing `forum_title` setting.
- No image proxying — the `og:image` URL is whatever sits in the first post's rendered HTML (e.g. a `fof/upload` attachment URL).

What it skips
-------------

[](#what-it-skips)

Defensively, the meta tags are **not** injected when the discussion is:

- Hidden (`isHidden = true`)
- Private (fof/byobu `isPrivateDiscussion = true`)
- Unapproved (flarum/approval `isApproved = false`)

Flarum's API already 404s these for unauthenticated viewers (which is what link-unfurl bots are), so the meta tags would never reach a crawler anyway — but the explicit skip belt-and-braces the case where a privileged user copies a non-public discussion URL.

Install
-------

[](#install)

```
composer require ekumanov/flarum-ext-discussion-og-meta
php flarum extension:enable ekumanov-discussion-og-meta
php flarum cache:clear
```

That's it — no settings to configure.

Verify
------

[](#verify)

After install, on any public discussion page:

```
curl -sS https://your-forum.example/d/123-some-slug \
  | grep -E 'content(AddOgMetaTags::class)` callback. It runs **after** Flarum's built-in discussion route handler has populated `$document->payload['apiDocument']` with the JSON:API document for the discussion (including the eager-loaded `firstPost`). So the callback adds no DB queries of its own — it just reads what core has already fetched and formats it into meta tags.

The description build pipeline:

1. Read `firstPost.attributes.contentHtml` from the API document.
2. Replace every HTML tag with a single space (so `` doesn't concatenate text), then `html_entity_decode`, then collapse whitespace.
3. Truncate at the last word boundary before 200 chars and append `…`.

The image scan:

1. Short-circuit if `contentHtml` doesn't contain `
