diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e87ebd..4676243 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Fix multilingual data corruption in content plugin load/save (#41) ### Added +- Fediverse/Mastodon `fediverse:creator` meta tag — first extension on any CMS to support this (#57) +- Live character count indicators on OG title, OG description, SEO title, meta description fields with color-coded warnings (#58) +- LinkedIn social preview card in article/menu editor alongside Facebook and Twitter/X previews (#61) - Site-wide default OG title and description plugin parameters - Discord embed color via `theme-color` meta tag (color picker in plugin config) - LinkedIn article tags: `article:published_time`, `article:modified_time`, `article:author` diff --git a/source/packages/plg_content_mokoog/media/css/preview.css b/source/packages/plg_content_mokoog/media/css/preview.css index 0ad41bb..ae0a8af 100644 --- a/source/packages/plg_content_mokoog/media/css/preview.css +++ b/source/packages/plg_content_mokoog/media/css/preview.css @@ -102,3 +102,46 @@ color: #536471; margin-top: 2px; } + +/* LinkedIn card */ +.mokoog-card-li { + border: 1px solid #e0dfdc; + border-radius: 2px; +} + +.mokoog-card-li .mokoog-card-body { + border-top-color: #e0dfdc; +} + +.mokoog-card-li .mokoog-card-title { + font-size: 14px; + font-weight: 600; + color: rgba(0, 0, 0, 0.9); +} + +.mokoog-card-li .mokoog-card-domain { + font-size: 12px; + color: rgba(0, 0, 0, 0.6); + text-transform: none; +} + +/* Character count indicators */ +.mokoog-char-count { + display: block; + font-size: 12px; + margin-top: 4px; + font-variant-numeric: tabular-nums; +} + +.mokoog-char-ok { + color: #2e7d32; +} + +.mokoog-char-warn { + color: #f57c00; +} + +.mokoog-char-over { + color: #d32f2f; + font-weight: 600; +} diff --git a/source/packages/plg_content_mokoog/media/js/preview.js b/source/packages/plg_content_mokoog/media/js/preview.js index a10c3f1..6cc4e1a 100644 --- a/source/packages/plg_content_mokoog/media/js/preview.js +++ b/source/packages/plg_content_mokoog/media/js/preview.js @@ -15,9 +15,44 @@ document.addEventListener('DOMContentLoaded', function () { ogDesc: document.getElementById('jform_mokoog_og_description'), ogImage: document.getElementById('jform_mokoog_og_image'), articleTitle: document.getElementById('jform_title'), - metaDesc: document.getElementById('jform_metadesc') + metaDesc: document.getElementById('jform_metadesc'), + seoTitle: document.getElementById('jform_mokoog_seo_title'), + metaDescription: document.getElementById('jform_mokoog_meta_description') }; + // Character count indicators + var charLimits = [ + { field: fields.ogTitle, optimal: 60, max: 90 }, + { field: fields.ogDesc, optimal: 155, max: 200 }, + { field: fields.seoTitle, optimal: 60, max: 70 }, + { field: fields.metaDescription, optimal: 155, max: 160 } + ]; + + charLimits.forEach(function (cfg) { + if (!cfg.field) return; + + var counter = document.createElement('span'); + counter.className = 'mokoog-char-count'; + cfg.field.parentNode.appendChild(counter); + + function refresh() { + var len = cfg.field.value.length; + counter.textContent = len + '/' + cfg.optimal; + + if (len > cfg.max) { + counter.className = 'mokoog-char-count mokoog-char-over'; + } else if (len > cfg.optimal) { + counter.className = 'mokoog-char-count mokoog-char-warn'; + } else { + counter.className = 'mokoog-char-count mokoog-char-ok'; + } + } + + cfg.field.addEventListener('input', refresh); + cfg.field.addEventListener('change', refresh); + refresh(); + }); + // Find the mokoog fieldset and insert preview after it var fieldset = document.querySelector('[data-showon-id="mokoog"]') || document.getElementById('attrib-mokoog') || @@ -110,6 +145,36 @@ document.addEventListener('DOMContentLoaded', function () { twCard.appendChild(twBody); wrapper.appendChild(twCard); + // LinkedIn preview card + var liLabel = document.createElement('small'); + liLabel.className = 'mokoog-platform-label'; + liLabel.textContent = 'LinkedIn'; + wrapper.appendChild(liLabel); + + var liCard = document.createElement('div'); + liCard.className = 'mokoog-card mokoog-card-li'; + + var liImg = document.createElement('div'); + liImg.id = 'mokoog-li-img'; + liImg.className = 'mokoog-card-img'; + liCard.appendChild(liImg); + + var liBody = document.createElement('div'); + liBody.className = 'mokoog-card-body'; + + var liTitle = document.createElement('div'); + liTitle.id = 'mokoog-li-title'; + liTitle.className = 'mokoog-card-title'; + liBody.appendChild(liTitle); + + var liDomain = document.createElement('div'); + liDomain.id = 'mokoog-li-domain'; + liDomain.className = 'mokoog-card-domain'; + liBody.appendChild(liDomain); + + liCard.appendChild(liBody); + wrapper.appendChild(liCard); + preview.appendChild(wrapper); fieldset.parentNode.insertBefore(preview, fieldset.nextSibling); @@ -152,6 +217,18 @@ document.addEventListener('DOMContentLoaded', function () { } else { twImgEl.style.display = 'none'; } + + // LinkedIn (shorter truncation: title 70, no description shown in card) + var liTitle = title.length > 70 ? title.substring(0, 67) + '...' : title; + document.getElementById('mokoog-li-title').textContent = liTitle; + document.getElementById('mokoog-li-domain').textContent = domain; + var liImgEl = document.getElementById('mokoog-li-img'); + if (img) { + liImgEl.style.backgroundImage = 'url(' + encodeURI(img) + ')'; + liImgEl.style.display = ''; + } else { + liImgEl.style.display = 'none'; + } } Object.values(fields).forEach(function (el) { diff --git a/source/packages/plg_system_mokoog/language/en-GB/plg_system_mokoog.ini b/source/packages/plg_system_mokoog/language/en-GB/plg_system_mokoog.ini index 3d1c0f6..e6fdb6b 100644 --- a/source/packages/plg_system_mokoog/language/en-GB/plg_system_mokoog.ini +++ b/source/packages/plg_system_mokoog/language/en-GB/plg_system_mokoog.ini @@ -23,6 +23,8 @@ PLG_SYSTEM_MOKOOG_FIELD_FB_APP_ID="Facebook App ID" PLG_SYSTEM_MOKOOG_FIELD_FB_APP_ID_DESC="Your Facebook App ID for fb:app_id meta tag." PLG_SYSTEM_MOKOOG_FIELD_DISCORD_COLOR="Discord Embed Color" PLG_SYSTEM_MOKOOG_FIELD_DISCORD_COLOR_DESC="The color of the embed sidebar when shared on Discord. Sets the theme-color meta tag. Leave blank to use Discord defaults." +PLG_SYSTEM_MOKOOG_FIELD_FEDIVERSE_CREATOR="Fediverse Creator" +PLG_SYSTEM_MOKOOG_FIELD_FEDIVERSE_CREATOR_DESC="Your Fediverse/Mastodon handle (e.g. @user@mastodon.social). Outputs a fediverse:creator meta tag for author attribution on Mastodon and other Fediverse platforms." PLG_SYSTEM_MOKOOG_FIELD_AUTO_GENERATE="Auto-generate Tags" PLG_SYSTEM_MOKOOG_FIELD_AUTO_GENERATE_DESC="Automatically generate OG tags from article content when no custom tags are set." PLG_SYSTEM_MOKOOG_FIELD_STRIP_HTML="Strip HTML from Description" diff --git a/source/packages/plg_system_mokoog/language/en-US/plg_system_mokoog.ini b/source/packages/plg_system_mokoog/language/en-US/plg_system_mokoog.ini index 3d1c0f6..e6fdb6b 100644 --- a/source/packages/plg_system_mokoog/language/en-US/plg_system_mokoog.ini +++ b/source/packages/plg_system_mokoog/language/en-US/plg_system_mokoog.ini @@ -23,6 +23,8 @@ PLG_SYSTEM_MOKOOG_FIELD_FB_APP_ID="Facebook App ID" PLG_SYSTEM_MOKOOG_FIELD_FB_APP_ID_DESC="Your Facebook App ID for fb:app_id meta tag." PLG_SYSTEM_MOKOOG_FIELD_DISCORD_COLOR="Discord Embed Color" PLG_SYSTEM_MOKOOG_FIELD_DISCORD_COLOR_DESC="The color of the embed sidebar when shared on Discord. Sets the theme-color meta tag. Leave blank to use Discord defaults." +PLG_SYSTEM_MOKOOG_FIELD_FEDIVERSE_CREATOR="Fediverse Creator" +PLG_SYSTEM_MOKOOG_FIELD_FEDIVERSE_CREATOR_DESC="Your Fediverse/Mastodon handle (e.g. @user@mastodon.social). Outputs a fediverse:creator meta tag for author attribution on Mastodon and other Fediverse platforms." PLG_SYSTEM_MOKOOG_FIELD_AUTO_GENERATE="Auto-generate Tags" PLG_SYSTEM_MOKOOG_FIELD_AUTO_GENERATE_DESC="Automatically generate OG tags from article content when no custom tags are set." PLG_SYSTEM_MOKOOG_FIELD_STRIP_HTML="Strip HTML from Description" diff --git a/source/packages/plg_system_mokoog/mokoog.xml b/source/packages/plg_system_mokoog/mokoog.xml index 759220c..d6ea7e0 100644 --- a/source/packages/plg_system_mokoog/mokoog.xml +++ b/source/packages/plg_system_mokoog/mokoog.xml @@ -106,6 +106,14 @@ description="PLG_SYSTEM_MOKOOG_FIELD_DISCORD_COLOR_DESC" default="" /> +
setMetaData('theme-color', $discordColor); } + // Fediverse/Mastodon creator attribution + $fediverseCreator = $this->params->get('fediverse_creator', ''); + + if ($fediverseCreator) { + $doc->setMetaData('fediverse:creator', $fediverseCreator); + } + // LinkedIn article tags if ($option === 'com_content' && $view === 'article' && $id > 0) { $doc->setMetaData('article:published_time', $this->getArticleDate($id, 'publish_up'), 'property');