Merge branch 'feature/3-social-preview' into dev

This commit is contained in:
Jonathan Miller
2026-05-23 22:08:07 -05:00
5 changed files with 306 additions and 0 deletions
@@ -0,0 +1,104 @@
/**
* @package MokoOpenGraph
* @subpackage plg_content_mokoog
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
* @license GPL-3.0-or-later
*/
.mokoog-preview-wrapper {
margin: 15px 0;
padding: 15px;
background: #f8f9fa;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.mokoog-preview-heading {
margin: 0 0 12px;
font-size: 14px;
color: #666;
}
.mokoog-platform-label {
display: block;
color: #999;
text-transform: uppercase;
font-size: 11px;
font-weight: 600;
margin-top: 16px;
margin-bottom: 4px;
}
.mokoog-platform-label:first-of-type {
margin-top: 0;
}
.mokoog-card {
overflow: hidden;
max-width: 500px;
background: #fff;
}
.mokoog-card-fb {
border: 1px solid #ddd;
border-radius: 3px;
}
.mokoog-card-tw {
border: 1px solid #cfd9de;
border-radius: 16px;
}
.mokoog-card-img {
height: 260px;
background: #e4e6eb center / cover no-repeat;
}
.mokoog-card-body {
padding: 10px 12px;
border-top: 1px solid #ddd;
}
.mokoog-card-tw .mokoog-card-body {
border-top-color: #cfd9de;
}
.mokoog-card-domain {
font-size: 11px;
color: #65676b;
text-transform: uppercase;
}
.mokoog-card-tw .mokoog-card-domain {
font-size: 13px;
text-transform: none;
margin-top: 4px;
}
.mokoog-card-title {
font-size: 16px;
font-weight: 600;
color: #1d2129;
margin: 3px 0 2px;
line-height: 1.3;
}
.mokoog-card-tw .mokoog-card-title {
font-size: 15px;
font-weight: 700;
color: #0f1419;
}
.mokoog-card-desc {
font-size: 14px;
color: #65676b;
line-height: 1.4;
max-height: 2.8em;
overflow: hidden;
}
.mokoog-card-tw .mokoog-card-desc {
font-size: 15px;
color: #536471;
margin-top: 2px;
}
@@ -0,0 +1,20 @@
{
"$schema": "https://developer.joomla.org/schemas/json-schema/web_assets.json",
"name": "plg_content_mokoog",
"version": "01.00.00",
"description": "MokoOpenGraph Content Plugin Assets",
"license": "GPL-3.0-or-later",
"assets": [
{
"name": "plg_content_mokoog.preview",
"type": "style",
"uri": "plg_content_mokoog/css/preview.css"
},
{
"name": "plg_content_mokoog.preview",
"type": "script",
"uri": "plg_content_mokoog/js/preview.js",
"dependencies": ["core"]
}
]
}
@@ -0,0 +1,170 @@
/**
* @package MokoOpenGraph
* @subpackage plg_content_mokoog
* @author Moko Consulting <hello@mokoconsulting.tech>
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
* @license GPL-3.0-or-later
*
* Live social sharing preview for article/menu item editor.
*/
document.addEventListener('DOMContentLoaded', function () {
'use strict';
var fields = {
ogTitle: document.getElementById('jform_mokoog_og_title'),
ogDesc: document.getElementById('jform_mokoog_og_description'),
ogImage: document.getElementById('jform_mokoog_og_image'),
articleTitle: document.getElementById('jform_title'),
metaDesc: document.getElementById('jform_metadesc')
};
// Find the mokoog fieldset and insert preview after it
var fieldset = document.querySelector('[data-showon-id="mokoog"]') ||
document.getElementById('attrib-mokoog') ||
document.querySelector('fieldset.mokoog') ||
document.querySelector('[id*="mokoog"]');
if (!fieldset) {
return;
}
// Build preview DOM safely (no innerHTML with user data)
var preview = document.createElement('div');
preview.id = 'mokoog-preview';
var wrapper = document.createElement('div');
wrapper.className = 'mokoog-preview-wrapper';
var heading = document.createElement('h4');
heading.className = 'mokoog-preview-heading';
heading.textContent = 'Social Sharing Preview';
wrapper.appendChild(heading);
// Facebook preview card
var fbLabel = document.createElement('small');
fbLabel.className = 'mokoog-platform-label';
fbLabel.textContent = 'Facebook';
wrapper.appendChild(fbLabel);
var fbCard = document.createElement('div');
fbCard.className = 'mokoog-card mokoog-card-fb';
var fbImg = document.createElement('div');
fbImg.id = 'mokoog-fb-img';
fbImg.className = 'mokoog-card-img';
fbCard.appendChild(fbImg);
var fbBody = document.createElement('div');
fbBody.className = 'mokoog-card-body';
var fbDomain = document.createElement('div');
fbDomain.id = 'mokoog-fb-domain';
fbDomain.className = 'mokoog-card-domain';
fbBody.appendChild(fbDomain);
var fbTitle = document.createElement('div');
fbTitle.id = 'mokoog-fb-title';
fbTitle.className = 'mokoog-card-title';
fbBody.appendChild(fbTitle);
var fbDesc = document.createElement('div');
fbDesc.id = 'mokoog-fb-desc';
fbDesc.className = 'mokoog-card-desc';
fbBody.appendChild(fbDesc);
fbCard.appendChild(fbBody);
wrapper.appendChild(fbCard);
// Twitter preview card
var twLabel = document.createElement('small');
twLabel.className = 'mokoog-platform-label';
twLabel.textContent = 'Twitter / X';
wrapper.appendChild(twLabel);
var twCard = document.createElement('div');
twCard.className = 'mokoog-card mokoog-card-tw';
var twImg = document.createElement('div');
twImg.id = 'mokoog-tw-img';
twImg.className = 'mokoog-card-img';
twCard.appendChild(twImg);
var twBody = document.createElement('div');
twBody.className = 'mokoog-card-body';
var twTitle = document.createElement('div');
twTitle.id = 'mokoog-tw-title';
twTitle.className = 'mokoog-card-title';
twBody.appendChild(twTitle);
var twDesc = document.createElement('div');
twDesc.id = 'mokoog-tw-desc';
twDesc.className = 'mokoog-card-desc';
twBody.appendChild(twDesc);
var twDomain = document.createElement('div');
twDomain.id = 'mokoog-tw-domain';
twDomain.className = 'mokoog-card-domain';
twBody.appendChild(twDomain);
twCard.appendChild(twBody);
wrapper.appendChild(twCard);
preview.appendChild(wrapper);
fieldset.parentNode.insertBefore(preview, fieldset.nextSibling);
var domain = window.location.hostname;
function updatePreview() {
var title = (fields.ogTitle && fields.ogTitle.value) ||
(fields.articleTitle && fields.articleTitle.value) || 'Page Title';
var desc = (fields.ogDesc && fields.ogDesc.value) ||
(fields.metaDesc && fields.metaDesc.value) || 'Page description will appear here...';
var img = '';
if (fields.ogImage) {
img = fields.ogImage.value;
}
if (title.length > 65) title = title.substring(0, 62) + '...';
if (desc.length > 160) desc = desc.substring(0, 157) + '...';
// Facebook
document.getElementById('mokoog-fb-title').textContent = title;
document.getElementById('mokoog-fb-desc').textContent = desc;
document.getElementById('mokoog-fb-domain').textContent = domain;
var fbImgEl = document.getElementById('mokoog-fb-img');
if (img) {
fbImgEl.style.backgroundImage = 'url(' + encodeURI(img) + ')';
fbImgEl.style.display = '';
} else {
fbImgEl.style.display = 'none';
}
// Twitter
document.getElementById('mokoog-tw-title').textContent = title;
document.getElementById('mokoog-tw-desc').textContent = desc;
document.getElementById('mokoog-tw-domain').textContent = domain;
var twImgEl = document.getElementById('mokoog-tw-img');
if (img) {
twImgEl.style.backgroundImage = 'url(' + encodeURI(img) + ')';
twImgEl.style.display = '';
} else {
twImgEl.style.display = 'none';
}
}
Object.values(fields).forEach(function (el) {
if (el) {
el.addEventListener('input', updatePreview);
el.addEventListener('change', updatePreview);
}
});
if (fields.ogImage) {
var observer = new MutationObserver(updatePreview);
observer.observe(fields.ogImage, { attributes: true, attributeFilter: ['value'] });
}
updatePreview();
});
@@ -27,6 +27,12 @@
<folder>language</folder>
</files>
<media destination="plg_content_mokoog" folder="media">
<filename>joomla.asset.json</filename>
<folder>js</folder>
<folder>css</folder>
</media>
<languages>
<language tag="en-GB">language/en-GB/plg_content_mokoog.ini</language>
<language tag="en-GB">language/en-GB/plg_content_mokoog.sys.ini</language>
@@ -72,6 +72,12 @@ final class MokoOGContent extends CMSPlugin implements SubscriberInterface
Form::addFormPath($formPath);
$form->loadFile('mokoog', false);
// Load live preview assets
$wa = $this->getApplication()->getDocument()->getWebAssetManager();
$wa->getRegistry()->addRegistryFile('media/plg_content_mokoog/joomla.asset.json');
$wa->useStyle('plg_content_mokoog.preview');
$wa->useScript('plg_content_mokoog.preview');
// If editing an existing item, load saved OG data
$id = 0;