✨ feat: editor-defined OG image and description#650
Merged
Conversation
Adds per-entity Open Graph image and description controls so editors can override how decks, archetypes, archetype variants, and the Banned/Staple Cards listing pages appear when shared on social platforms. - New nullable columns: deck.og_image/og_description (non-translatable), archetype_translation.og_image/og_description (per-locale, independent from meta_description so length can be tuned for social cards), page_translation.og_image/og_description (per-locale override on top of the existing parent-level Page.ogImage). - New App\Service\Seo\OgMetaResolver centralises the fallback chain: deck own values -> (variant?) archetype translation -> mosaic image; archetype translation -> localizedMetaDescription fallback for desc; page translation -> parent page -> none. - DeckShowController, ArchetypeDetailController, BannedCardController, StapleCardController inject the resolver and pass the resolved values into their templates. AdminPageController.duplicate() now copies the per-locale OG fields when cloning a page. - Templates emit the new vars through the existing _partials/opengraph.html.twig (already null-guarded); staple_card/list gains a full opengraph block (it had none). - Admin forms render the new ogImage inputs with the existing ImageUrlField drag-and-drop React component via the admin/_image_url_field.html.twig macro. The React mount loop is extracted to assets/shared/mount-image-url-field.tsx and reused from page-form, deck-form, and archetype-form entries. - 13 new unit tests in OgMetaResolverTest cover variant fallback, archetype meta-description fallback, page translation/parent precedence, and missing-translation paths. 2 new functional tests on BannedCardController assert the og:image/og:description meta tags render from editor input and degrade gracefully when blank. - docs/features.md, docs/models/deck.md, docs/models/cms.md updated. Closes #580, closes #581.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Editor-defined Open Graph image and description controls across decks, archetypes, archetype variants, and the Banned/Staple Cards listing pages — bundling F18.30 and F18.31 because they share an
OgMetaResolverservice and a single PR is browser-validatable end-to-end.deck.og_image/og_description(non-translatable);archetype_translation.og_image/og_description(per-locale, kept independent frommeta_descriptionso length is tunable for social cards);page_translation.og_image/og_description(per-locale override on top of the existing parent-levelPage.ogImage). Migration is hand-pruned to only these sixADD COLUMNs — the auto-generated diff carried 30+ unrelated drift lines that have been stripped.App\Service\Seo\OgMetaResolver(stateless, no injected deps) centralises the fallback chain. For decks: own values → (ifisArchetypeVariant()) archetype translation →currentVersion.mosaicImageUrl. For archetypes: translation OG → graceful fallback tolocalizedMetaDescription. For pages: translation override → parentPage.ogImage→ none.DeckShowController,ArchetypeDetailController,BannedCardController,StapleCardControllerinject the resolver and pass the resolvedogImage/ogDescriptioninto their templates.AdminPageController::duplicate()also copies the new per-locale fields when cloning a page._partials/opengraph.html.twigalready null-guards both fields, so no partial change.staple_card/list.html.twiggains a full{% block opengraph %}block (it had none). The other three templates feed the new vars through their existing block.ogImageinputs render with the existingImageUrlFieldMantine drag-and-drop component via theadmin/_image_url_field.html.twigmacro — the same UX as the homepage editor and the parent-levelPage.ogImage. The React mount loop is extracted toassets/shared/mount-image-url-field.tsxand reused frompage-form,deck-form, andarchetype-formWebpack entries (page-form's previously-inline copy was replaced by the same call).messages.{en,fr}.xlf(app.form.label.og_*,app.archetype.og_*_label/help,app.cms.form.og_*_localized*). Per-localeog_image_localizedis kept distinct from the parent-levelapp.cms.form.og_imageso editors don't see the same label twice on the same admin page.OgMetaResolverTestcover variant fallback, meta-description graceful fallback, page translation/parent precedence, and missing-translation paths. 2 new functional tests onBannedCardControllerassert thatog:image/og:descriptionrender from editor input and degrade gracefully when blank.docs/features.md(new F18.30 and F18.31 rows),docs/models/deck.md,docs/models/cms.md.One design call worth flagging
og_titlefor Banned/Staple keeps the existing'app.banned_card.public.title'|transstyle rather than reusingPageTranslation.title, becausePageTranslationFormTypehides the title field whenis_listing_introis true (lines 35-39) — sotitlefor listing intros isn't editor-controlled. If editors want a separate social-share title later, the upgrade isPageTranslation.ogTitle.Test plan
og:image+og:descriptionreflect the editor values./en/archetypes/<slug>and/fr/archetypes/<slug>each render their locale-specific OG values./en/banned-cardsand/fr/banned-cardsemit correct meta. Repeat for staple cards (/en/staple-cards,/fr/staple-cards).og:image, the archetype page still emitslocalizedMetaDescriptionasog:description, and the banned/staple pages continue to emitog:title+og:urlas before.not-a-url) into the OG image field and confirm the regex validation (#^(/|https?://)#) rejects on submit.Closes #580, closes #581.