From d4c22ebdbf0e678a637c0ea954c7e1bd649d669f Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Tue, 23 Jun 2026 16:03:32 +0000 Subject: [PATCH 01/41] chore(version): auto-bump patch 01.03.06-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 2 +- README.md | 2 +- source/packages/com_mokoog/mokoog.xml | 2 +- source/packages/plg_content_mokoog/mokoog.xml | 2 +- source/packages/plg_system_mokoog/mokoog.xml | 2 +- source/packages/plg_webservices_mokoog/mokoog.xml | 2 +- source/pkg_mokoog.xml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index e4535d7..36cc567 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.03.05 +# VERSION: 01.03.06 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index 892fd0b..8f89a49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog - + All notable changes to MokoSuiteOpenGraph will be documented in this file. diff --git a/README.md b/README.md index cdd008e..2759604 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoSuiteOpenGraph - + Open Graph, Twitter Card, and social sharing meta tag management for Joomla 4/5/6. diff --git a/source/packages/com_mokoog/mokoog.xml b/source/packages/com_mokoog/mokoog.xml index 3eaa256..ffc1863 100644 --- a/source/packages/com_mokoog/mokoog.xml +++ b/source/packages/com_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> com_mokoog - 01.03.05 + 01.03.06 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokoog/mokoog.xml b/source/packages/plg_content_mokoog/mokoog.xml index 76f8a7f..cb0d9a0 100644 --- a/source/packages/plg_content_mokoog/mokoog.xml +++ b/source/packages/plg_content_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> Content - MokoJoomOpenGraph - 01.03.05 + 01.03.06 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokoog/mokoog.xml b/source/packages/plg_system_mokoog/mokoog.xml index aade14b..86854b9 100644 --- a/source/packages/plg_system_mokoog/mokoog.xml +++ b/source/packages/plg_system_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> System - MokoJoomOpenGraph - 01.03.05 + 01.03.06 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokoog/mokoog.xml b/source/packages/plg_webservices_mokoog/mokoog.xml index 437e768..ff5f380 100644 --- a/source/packages/plg_webservices_mokoog/mokoog.xml +++ b/source/packages/plg_webservices_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> Web Services - MokoJoomOpenGraph - 01.03.05 + 01.03.06 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokoog.xml b/source/pkg_mokoog.xml index 89c76ba..03f5686 100644 --- a/source/pkg_mokoog.xml +++ b/source/pkg_mokoog.xml @@ -8,7 +8,7 @@ Package - MokoSuiteOpenGraph mokoog - 01.03.05 + 01.03.06 2026-05-23 Moko Consulting hello@mokoconsulting.tech From e939e90733ff9caecda3ae6656d473dd1eea20e7 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Tue, 23 Jun 2026 16:03:42 +0000 Subject: [PATCH 02/41] chore(version): pre-release bump to 01.03.07-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 2 +- README.md | 2 +- source/packages/com_mokoog/mokoog.xml | 2 +- source/packages/plg_content_mokoog/mokoog.xml | 2 +- source/packages/plg_system_mokoog/mokoog.xml | 2 +- source/packages/plg_webservices_mokoog/mokoog.xml | 2 +- source/pkg_mokoog.xml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 36cc567..53040d4 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.03.06 +# VERSION: 01.03.07 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f89a49..e50c456 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog - + All notable changes to MokoSuiteOpenGraph will be documented in this file. diff --git a/README.md b/README.md index 2759604..52eed3f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoSuiteOpenGraph - + Open Graph, Twitter Card, and social sharing meta tag management for Joomla 4/5/6. diff --git a/source/packages/com_mokoog/mokoog.xml b/source/packages/com_mokoog/mokoog.xml index ffc1863..ad61104 100644 --- a/source/packages/com_mokoog/mokoog.xml +++ b/source/packages/com_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> com_mokoog - 01.03.06 + 01.03.07 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokoog/mokoog.xml b/source/packages/plg_content_mokoog/mokoog.xml index cb0d9a0..688da2a 100644 --- a/source/packages/plg_content_mokoog/mokoog.xml +++ b/source/packages/plg_content_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> Content - MokoJoomOpenGraph - 01.03.06 + 01.03.07 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokoog/mokoog.xml b/source/packages/plg_system_mokoog/mokoog.xml index 86854b9..4bae4a6 100644 --- a/source/packages/plg_system_mokoog/mokoog.xml +++ b/source/packages/plg_system_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> System - MokoJoomOpenGraph - 01.03.06 + 01.03.07 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokoog/mokoog.xml b/source/packages/plg_webservices_mokoog/mokoog.xml index ff5f380..a70784a 100644 --- a/source/packages/plg_webservices_mokoog/mokoog.xml +++ b/source/packages/plg_webservices_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> Web Services - MokoJoomOpenGraph - 01.03.06 + 01.03.07 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokoog.xml b/source/pkg_mokoog.xml index 03f5686..b55d457 100644 --- a/source/pkg_mokoog.xml +++ b/source/pkg_mokoog.xml @@ -8,7 +8,7 @@ Package - MokoSuiteOpenGraph mokoog - 01.03.06 + 01.03.07 2026-05-23 Moko Consulting hello@mokoconsulting.tech From b4d5b73d15377fb9766e502ccb2f0d2b1ecb7ffb Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 23 Jun 2026 11:25:27 -0500 Subject: [PATCH 03/41] fix: code quality improvements (#76, #77, #78, #79) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add exception logging to BatchController batch skip (#76) - Align form maxlength with DB schema limits (#77) - applySeoTags() already uses public API — no change needed (#78) - Add strip_tags() input sanitization on OG text fields (#79) --- source/packages/com_mokoog/forms/tag.xml | 8 ++++---- .../com_mokoog/src/Controller/BatchController.php | 1 + source/packages/plg_content_mokoog/forms/mokoog.xml | 8 ++++---- .../plg_content_mokoog/src/Extension/MokoOGContent.php | 8 ++++---- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/source/packages/com_mokoog/forms/tag.xml b/source/packages/com_mokoog/forms/tag.xml index 83f8f93..9eb8168 100644 --- a/source/packages/com_mokoog/forms/tag.xml +++ b/source/packages/com_mokoog/forms/tag.xml @@ -30,7 +30,7 @@ label="COM_MOKOOG_FIELD_OG_TITLE" description="COM_MOKOOG_FIELD_OG_TITLE_DESC" filter="string" - maxlength="70" + maxlength="255" /> id . ': ' . $e->getMessage(), \Joomla\CMS\Log\Log::WARNING, 'mokoog'); } } diff --git a/source/packages/plg_content_mokoog/forms/mokoog.xml b/source/packages/plg_content_mokoog/forms/mokoog.xml index 64caba3..b5168e3 100644 --- a/source/packages/plg_content_mokoog/forms/mokoog.xml +++ b/source/packages/plg_content_mokoog/forms/mokoog.xml @@ -16,7 +16,7 @@ label="PLG_CONTENT_MOKOOG_FIELD_OG_TITLE" description="PLG_CONTENT_MOKOOG_FIELD_OG_TITLE_DESC" filter="string" - maxlength="70" + maxlength="255" /> $contentType, 'content_id' => $contentId, 'language' => $language, - 'og_title' => trim($ogData['og_title'] ?? ''), - 'og_description' => trim($ogData['og_description'] ?? ''), + 'og_title' => strip_tags(trim($ogData['og_title'] ?? '')), + 'og_description' => strip_tags(trim($ogData['og_description'] ?? '')), 'og_image' => trim($ogData['og_image'] ?? ''), 'og_type' => trim($ogData['og_type'] ?? 'article'), 'og_video' => $this->sanitizeUrl($ogData['og_video'] ?? ''), - 'seo_title' => trim($ogData['seo_title'] ?? ''), - 'meta_description' => trim($ogData['meta_description'] ?? ''), + 'seo_title' => strip_tags(trim($ogData['seo_title'] ?? '')), + 'meta_description' => strip_tags(trim($ogData['meta_description'] ?? '')), 'robots' => trim($robots), 'canonical_url' => trim($ogData['canonical_url'] ?? ''), 'published' => 1, From aeea65423c3f892f485673ff3dc90b49e24b41c6 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 23 Jun 2026 11:24:56 -0500 Subject: [PATCH 04/41] feat: add VideoObject JSON-LD schema for video content Outputs VideoObject structured data when an article has a video URL set, enabling Google video rich results. Closes #67 --- .../src/Extension/MokoOG.php | 8 +++++ .../src/Helper/JsonLdBuilder.php | 34 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/source/packages/plg_system_mokoog/src/Extension/MokoOG.php b/source/packages/plg_system_mokoog/src/Extension/MokoOG.php index e65971f..e3fbb0e 100644 --- a/source/packages/plg_system_mokoog/src/Extension/MokoOG.php +++ b/source/packages/plg_system_mokoog/src/Extension/MokoOG.php @@ -282,6 +282,14 @@ final class MokoOG extends CMSPlugin implements SubscriberInterface $doc->addCustomTag(JsonLdBuilder::toScriptTag($schema)); } + if (!empty($ogData->og_video)) { + $videoSchema = JsonLdBuilder::buildVideo($ogData->og_video, $title, $description, $imageUrl); + + if ($videoSchema) { + $doc->addCustomTag(JsonLdBuilder::toScriptTag($videoSchema)); + } + } + if ($this->params->get('jsonld_breadcrumbs', 1)) { $breadcrumbs = JsonLdBuilder::buildBreadcrumbs(); diff --git a/source/packages/plg_system_mokoog/src/Helper/JsonLdBuilder.php b/source/packages/plg_system_mokoog/src/Helper/JsonLdBuilder.php index 7f0598a..1f304af 100644 --- a/source/packages/plg_system_mokoog/src/Helper/JsonLdBuilder.php +++ b/source/packages/plg_system_mokoog/src/Helper/JsonLdBuilder.php @@ -248,6 +248,40 @@ class JsonLdBuilder return $schema; } + /** + * Build VideoObject schema for pages with a video URL. + * + * @param string $videoUrl Video URL (e.g. YouTube, Vimeo, or direct) + * @param string $title Video title + * @param string $description Video description + * @param string $imageUrl Thumbnail image URL (absolute) + * + * @return array|null + */ + public static function buildVideo(string $videoUrl, string $title, string $description, string $imageUrl): ?array + { + if (empty($videoUrl)) { + return null; + } + + $schema = [ + '@context' => 'https://schema.org', + '@type' => 'VideoObject', + 'name' => $title, + 'description' => $description, + 'thumbnailUrl' => $imageUrl, + 'contentUrl' => $videoUrl, + 'uploadDate' => Factory::getDate()->toISO8601(), + ]; + + // Add embedUrl for YouTube and Vimeo + if (preg_match('/youtube\.com|youtu\.be|vimeo\.com/i', $videoUrl)) { + $schema['embedUrl'] = $videoUrl; + } + + return $schema; + } + /** * Encode a schema array to a JSON-LD script tag string. * From 96eea6060fbe09541730cb7a1bd22761778fcfbf Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 23 Jun 2026 11:27:18 -0500 Subject: [PATCH 05/41] feat: add Discord, Mastodon, and Slack preview cards to editor Shows how shared links will appear on Discord (dark theme with accent bar), Mastodon (rounded card), and Slack (compact unfurl) alongside the existing Facebook, Twitter, and LinkedIn previews. Closes #69 --- .../plg_content_mokoog/media/css/preview.css | 95 ++++++++++++ .../plg_content_mokoog/media/js/preview.js | 136 ++++++++++++++++++ 2 files changed, 231 insertions(+) diff --git a/source/packages/plg_content_mokoog/media/css/preview.css b/source/packages/plg_content_mokoog/media/css/preview.css index ae0a8af..7f7d915 100644 --- a/source/packages/plg_content_mokoog/media/css/preview.css +++ b/source/packages/plg_content_mokoog/media/css/preview.css @@ -125,6 +125,101 @@ text-transform: none; } +/* Discord card */ +.mokoog-card-dc { + background: #2b2d31; + border-left: 4px solid #5865f2; + border-radius: 4px; +} + +.mokoog-card-dc .mokoog-card-body { + border-top: none; +} + +.mokoog-card-dc .mokoog-card-img { + height: 200px; + margin: 0 12px 12px; + border-radius: 4px; +} + +.mokoog-card-dc .mokoog-card-title { + font-size: 16px; + font-weight: 700; + color: #00a8fc; +} + +.mokoog-card-dc .mokoog-card-desc { + font-size: 14px; + color: #dbdee1; +} + +.mokoog-card-dc .mokoog-card-domain { + font-size: 12px; + color: #b5bac1; + text-transform: none; +} + +/* Mastodon card */ +.mokoog-card-ma { + border: 1px solid #c8ccd0; + border-radius: 8px; +} + +.mokoog-card-ma .mokoog-card-img { + border-radius: 8px 8px 0 0; +} + +.mokoog-card-ma .mokoog-card-body { + border-top-color: #c8ccd0; +} + +.mokoog-card-ma .mokoog-card-title { + font-size: 14px; + font-weight: 600; + color: #1a1a2e; +} + +.mokoog-card-ma .mokoog-card-desc { + font-size: 13px; + color: #606984; +} + +.mokoog-card-ma .mokoog-card-domain { + font-size: 12px; + color: #606984; + text-transform: none; +} + +/* Slack card */ +.mokoog-card-sl { + border-left: 4px solid #36c5f0; + border-radius: 0; + background: #fff; +} + +.mokoog-card-sl .mokoog-card-body { + border-top: none; + padding: 8px 12px; +} + +.mokoog-card-sl .mokoog-card-title { + font-size: 15px; + font-weight: 700; + color: #1264a3; +} + +.mokoog-card-sl .mokoog-card-desc { + font-size: 14px; + color: #1d1c1d; +} + +.mokoog-card-sl .mokoog-card-domain { + font-size: 12px; + color: #616061; + text-transform: none; + margin-top: 4px; +} + /* Character count indicators */ .mokoog-char-count { display: block; diff --git a/source/packages/plg_content_mokoog/media/js/preview.js b/source/packages/plg_content_mokoog/media/js/preview.js index 6cc4e1a..8a17c70 100644 --- a/source/packages/plg_content_mokoog/media/js/preview.js +++ b/source/packages/plg_content_mokoog/media/js/preview.js @@ -175,6 +175,107 @@ document.addEventListener('DOMContentLoaded', function () { liCard.appendChild(liBody); wrapper.appendChild(liCard); + // Discord preview card + var dcLabel = document.createElement('small'); + dcLabel.className = 'mokoog-platform-label'; + dcLabel.textContent = 'Discord'; + wrapper.appendChild(dcLabel); + + var dcCard = document.createElement('div'); + dcCard.className = 'mokoog-card mokoog-card-dc'; + + var dcBody = document.createElement('div'); + dcBody.className = 'mokoog-card-body'; + + var dcTitle = document.createElement('div'); + dcTitle.id = 'mokoog-dc-title'; + dcTitle.className = 'mokoog-card-title'; + dcBody.appendChild(dcTitle); + + var dcDesc = document.createElement('div'); + dcDesc.id = 'mokoog-dc-desc'; + dcDesc.className = 'mokoog-card-desc'; + dcBody.appendChild(dcDesc); + + var dcDomain = document.createElement('div'); + dcDomain.id = 'mokoog-dc-domain'; + dcDomain.className = 'mokoog-card-domain'; + dcBody.appendChild(dcDomain); + + dcCard.appendChild(dcBody); + + var dcImg = document.createElement('div'); + dcImg.id = 'mokoog-dc-img'; + dcImg.className = 'mokoog-card-img'; + dcCard.appendChild(dcImg); + + wrapper.appendChild(dcCard); + + // Mastodon preview card + var maLabel = document.createElement('small'); + maLabel.className = 'mokoog-platform-label'; + maLabel.textContent = 'Mastodon'; + wrapper.appendChild(maLabel); + + var maCard = document.createElement('div'); + maCard.className = 'mokoog-card mokoog-card-ma'; + + var maImg = document.createElement('div'); + maImg.id = 'mokoog-ma-img'; + maImg.className = 'mokoog-card-img'; + maCard.appendChild(maImg); + + var maBody = document.createElement('div'); + maBody.className = 'mokoog-card-body'; + + var maTitle = document.createElement('div'); + maTitle.id = 'mokoog-ma-title'; + maTitle.className = 'mokoog-card-title'; + maBody.appendChild(maTitle); + + var maDesc = document.createElement('div'); + maDesc.id = 'mokoog-ma-desc'; + maDesc.className = 'mokoog-card-desc'; + maBody.appendChild(maDesc); + + var maDomain = document.createElement('div'); + maDomain.id = 'mokoog-ma-domain'; + maDomain.className = 'mokoog-card-domain'; + maBody.appendChild(maDomain); + + maCard.appendChild(maBody); + wrapper.appendChild(maCard); + + // Slack preview card + var slLabel = document.createElement('small'); + slLabel.className = 'mokoog-platform-label'; + slLabel.textContent = 'Slack'; + wrapper.appendChild(slLabel); + + var slCard = document.createElement('div'); + slCard.className = 'mokoog-card mokoog-card-sl'; + + var slBody = document.createElement('div'); + slBody.className = 'mokoog-card-body'; + + var slTitle = document.createElement('div'); + slTitle.id = 'mokoog-sl-title'; + slTitle.className = 'mokoog-card-title'; + slBody.appendChild(slTitle); + + var slDesc = document.createElement('div'); + slDesc.id = 'mokoog-sl-desc'; + slDesc.className = 'mokoog-card-desc'; + slBody.appendChild(slDesc); + + var slDomain = document.createElement('div'); + slDomain.id = 'mokoog-sl-domain'; + slDomain.className = 'mokoog-card-domain'; + slBody.appendChild(slDomain); + + slCard.appendChild(slBody); + wrapper.appendChild(slCard); + preview.appendChild(wrapper); fieldset.parentNode.insertBefore(preview, fieldset.nextSibling); @@ -229,6 +330,41 @@ document.addEventListener('DOMContentLoaded', function () { } else { liImgEl.style.display = 'none'; } + + // Discord (title 256, desc 350) + var dcTitle = title.length > 256 ? title.substring(0, 253) + '...' : title; + var dcDesc = desc.length > 350 ? desc.substring(0, 347) + '...' : desc; + document.getElementById('mokoog-dc-title').textContent = dcTitle; + document.getElementById('mokoog-dc-desc').textContent = dcDesc; + document.getElementById('mokoog-dc-domain').textContent = domain; + var dcImgEl = document.getElementById('mokoog-dc-img'); + if (img) { + dcImgEl.style.backgroundImage = 'url(' + encodeURI(img) + ')'; + dcImgEl.style.display = ''; + } else { + dcImgEl.style.display = 'none'; + } + + // Mastodon (title 70, desc 200) + var maTitle = title.length > 70 ? title.substring(0, 67) + '...' : title; + var maDesc = desc.length > 200 ? desc.substring(0, 197) + '...' : desc; + document.getElementById('mokoog-ma-title').textContent = maTitle; + document.getElementById('mokoog-ma-desc').textContent = maDesc; + document.getElementById('mokoog-ma-domain').textContent = domain; + var maImgEl = document.getElementById('mokoog-ma-img'); + if (img) { + maImgEl.style.backgroundImage = 'url(' + encodeURI(img) + ')'; + maImgEl.style.display = ''; + } else { + maImgEl.style.display = 'none'; + } + + // Slack (title 70, desc 150, no image) + var slTitle = title.length > 70 ? title.substring(0, 67) + '...' : title; + var slDesc = desc.length > 150 ? desc.substring(0, 147) + '...' : desc; + document.getElementById('mokoog-sl-title').textContent = slTitle; + document.getElementById('mokoog-sl-desc').textContent = slDesc; + document.getElementById('mokoog-sl-domain').textContent = domain; } Object.values(fields).forEach(function (el) { From 44d9daf3bc7edc3ed344c5178c3c43a7ad8ed553 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 23 Jun 2026 11:27:59 -0500 Subject: [PATCH 06/41] feat: add LocalBusiness JSON-LD schema type Adds configurable LocalBusiness structured data with address, contact, geo coordinates, and opening hours. Enabled via plugin parameters. Closes #65 --- .../language/en-GB/plg_system_mokoog.ini | 32 +++++ .../language/en-US/plg_system_mokoog.ini | 32 +++++ source/packages/plg_system_mokoog/mokoog.xml | 129 ++++++++++++++++++ .../src/Extension/MokoOG.php | 9 ++ .../src/Helper/JsonLdBuilder.php | 100 ++++++++++++++ 5 files changed, 302 insertions(+) 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 e6fdb6b..2c8029d 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 @@ -39,3 +39,35 @@ PLG_SYSTEM_MOKOOG_FIELD_JSONLD_ENABLED="Enable JSON-LD" PLG_SYSTEM_MOKOOG_FIELD_JSONLD_ENABLED_DESC="Output JSON-LD structured data (Article, WebPage) for Google rich results." PLG_SYSTEM_MOKOOG_FIELD_JSONLD_BREADCRUMBS="JSON-LD Breadcrumbs" PLG_SYSTEM_MOKOOG_FIELD_JSONLD_BREADCRUMBS_DESC="Output BreadcrumbList JSON-LD schema from Joomla's pathway." + +PLG_SYSTEM_MOKOOG_FIELDSET_LOCALBUSINESS="Local Business" +PLG_SYSTEM_MOKOOG_FIELD_LB_ENABLED="Enable LocalBusiness Schema" +PLG_SYSTEM_MOKOOG_FIELD_LB_ENABLED_DESC="Output LocalBusiness JSON-LD structured data on all pages." +PLG_SYSTEM_MOKOOG_FIELD_LB_NAME="Business Name" +PLG_SYSTEM_MOKOOG_FIELD_LB_NAME_DESC="Your business name for structured data." +PLG_SYSTEM_MOKOOG_FIELD_LB_TYPE="Business Type" +PLG_SYSTEM_MOKOOG_FIELD_LB_TYPE_DESC="Schema.org business type." +PLG_SYSTEM_MOKOOG_FIELD_LB_STREET="Street Address" +PLG_SYSTEM_MOKOOG_FIELD_LB_STREET_DESC="Street address of your business." +PLG_SYSTEM_MOKOOG_FIELD_LB_CITY="City" +PLG_SYSTEM_MOKOOG_FIELD_LB_CITY_DESC="City of your business." +PLG_SYSTEM_MOKOOG_FIELD_LB_REGION="State/Region" +PLG_SYSTEM_MOKOOG_FIELD_LB_REGION_DESC="State or region of your business." +PLG_SYSTEM_MOKOOG_FIELD_LB_POSTAL="Postal Code" +PLG_SYSTEM_MOKOOG_FIELD_LB_POSTAL_DESC="Postal/ZIP code of your business." +PLG_SYSTEM_MOKOOG_FIELD_LB_COUNTRY="Country" +PLG_SYSTEM_MOKOOG_FIELD_LB_COUNTRY_DESC="Country code (e.g. US, GB, DE)." +PLG_SYSTEM_MOKOOG_FIELD_LB_PHONE="Phone" +PLG_SYSTEM_MOKOOG_FIELD_LB_PHONE_DESC="Business phone number." +PLG_SYSTEM_MOKOOG_FIELD_LB_EMAIL="Email" +PLG_SYSTEM_MOKOOG_FIELD_LB_EMAIL_DESC="Business email address." +PLG_SYSTEM_MOKOOG_FIELD_LB_URL="Website URL" +PLG_SYSTEM_MOKOOG_FIELD_LB_URL_DESC="Business website URL." +PLG_SYSTEM_MOKOOG_FIELD_LB_OPENING_HOURS="Opening Hours" +PLG_SYSTEM_MOKOOG_FIELD_LB_OPENING_HOURS_DESC="Opening hours in schema.org format (e.g. Mo-Fr 09:00-17:00)." +PLG_SYSTEM_MOKOOG_FIELD_LB_LATITUDE="Latitude" +PLG_SYSTEM_MOKOOG_FIELD_LB_LATITUDE_DESC="Geographic latitude of your business." +PLG_SYSTEM_MOKOOG_FIELD_LB_LONGITUDE="Longitude" +PLG_SYSTEM_MOKOOG_FIELD_LB_LONGITUDE_DESC="Geographic longitude of your business." +PLG_SYSTEM_MOKOOG_FIELD_LB_PRICE_RANGE="Price Range" +PLG_SYSTEM_MOKOOG_FIELD_LB_PRICE_RANGE_DESC="Price range indicator (e.g. $, $$, $$$)." 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 e6fdb6b..2c8029d 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 @@ -39,3 +39,35 @@ PLG_SYSTEM_MOKOOG_FIELD_JSONLD_ENABLED="Enable JSON-LD" PLG_SYSTEM_MOKOOG_FIELD_JSONLD_ENABLED_DESC="Output JSON-LD structured data (Article, WebPage) for Google rich results." PLG_SYSTEM_MOKOOG_FIELD_JSONLD_BREADCRUMBS="JSON-LD Breadcrumbs" PLG_SYSTEM_MOKOOG_FIELD_JSONLD_BREADCRUMBS_DESC="Output BreadcrumbList JSON-LD schema from Joomla's pathway." + +PLG_SYSTEM_MOKOOG_FIELDSET_LOCALBUSINESS="Local Business" +PLG_SYSTEM_MOKOOG_FIELD_LB_ENABLED="Enable LocalBusiness Schema" +PLG_SYSTEM_MOKOOG_FIELD_LB_ENABLED_DESC="Output LocalBusiness JSON-LD structured data on all pages." +PLG_SYSTEM_MOKOOG_FIELD_LB_NAME="Business Name" +PLG_SYSTEM_MOKOOG_FIELD_LB_NAME_DESC="Your business name for structured data." +PLG_SYSTEM_MOKOOG_FIELD_LB_TYPE="Business Type" +PLG_SYSTEM_MOKOOG_FIELD_LB_TYPE_DESC="Schema.org business type." +PLG_SYSTEM_MOKOOG_FIELD_LB_STREET="Street Address" +PLG_SYSTEM_MOKOOG_FIELD_LB_STREET_DESC="Street address of your business." +PLG_SYSTEM_MOKOOG_FIELD_LB_CITY="City" +PLG_SYSTEM_MOKOOG_FIELD_LB_CITY_DESC="City of your business." +PLG_SYSTEM_MOKOOG_FIELD_LB_REGION="State/Region" +PLG_SYSTEM_MOKOOG_FIELD_LB_REGION_DESC="State or region of your business." +PLG_SYSTEM_MOKOOG_FIELD_LB_POSTAL="Postal Code" +PLG_SYSTEM_MOKOOG_FIELD_LB_POSTAL_DESC="Postal/ZIP code of your business." +PLG_SYSTEM_MOKOOG_FIELD_LB_COUNTRY="Country" +PLG_SYSTEM_MOKOOG_FIELD_LB_COUNTRY_DESC="Country code (e.g. US, GB, DE)." +PLG_SYSTEM_MOKOOG_FIELD_LB_PHONE="Phone" +PLG_SYSTEM_MOKOOG_FIELD_LB_PHONE_DESC="Business phone number." +PLG_SYSTEM_MOKOOG_FIELD_LB_EMAIL="Email" +PLG_SYSTEM_MOKOOG_FIELD_LB_EMAIL_DESC="Business email address." +PLG_SYSTEM_MOKOOG_FIELD_LB_URL="Website URL" +PLG_SYSTEM_MOKOOG_FIELD_LB_URL_DESC="Business website URL." +PLG_SYSTEM_MOKOOG_FIELD_LB_OPENING_HOURS="Opening Hours" +PLG_SYSTEM_MOKOOG_FIELD_LB_OPENING_HOURS_DESC="Opening hours in schema.org format (e.g. Mo-Fr 09:00-17:00)." +PLG_SYSTEM_MOKOOG_FIELD_LB_LATITUDE="Latitude" +PLG_SYSTEM_MOKOOG_FIELD_LB_LATITUDE_DESC="Geographic latitude of your business." +PLG_SYSTEM_MOKOOG_FIELD_LB_LONGITUDE="Longitude" +PLG_SYSTEM_MOKOOG_FIELD_LB_LONGITUDE_DESC="Geographic longitude of your business." +PLG_SYSTEM_MOKOOG_FIELD_LB_PRICE_RANGE="Price Range" +PLG_SYSTEM_MOKOOG_FIELD_LB_PRICE_RANGE_DESC="Price range indicator (e.g. $, $$, $$$)." diff --git a/source/packages/plg_system_mokoog/mokoog.xml b/source/packages/plg_system_mokoog/mokoog.xml index 4bae4a6..d87c2a5 100644 --- a/source/packages/plg_system_mokoog/mokoog.xml +++ b/source/packages/plg_system_mokoog/mokoog.xml @@ -181,6 +181,135 @@ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/source/packages/plg_system_mokoog/src/Extension/MokoOG.php b/source/packages/plg_system_mokoog/src/Extension/MokoOG.php index e3fbb0e..05d0bd3 100644 --- a/source/packages/plg_system_mokoog/src/Extension/MokoOG.php +++ b/source/packages/plg_system_mokoog/src/Extension/MokoOG.php @@ -298,6 +298,15 @@ final class MokoOG extends CMSPlugin implements SubscriberInterface } } } + + // LocalBusiness JSON-LD + if ($this->params->get('lb_enabled', 0)) { + $lbSchema = JsonLdBuilder::buildLocalBusiness($this->params); + + if ($lbSchema) { + $doc->addCustomTag(JsonLdBuilder::toScriptTag($lbSchema)); + } + } } /** diff --git a/source/packages/plg_system_mokoog/src/Helper/JsonLdBuilder.php b/source/packages/plg_system_mokoog/src/Helper/JsonLdBuilder.php index 1f304af..649bf07 100644 --- a/source/packages/plg_system_mokoog/src/Helper/JsonLdBuilder.php +++ b/source/packages/plg_system_mokoog/src/Helper/JsonLdBuilder.php @@ -282,6 +282,106 @@ class JsonLdBuilder return $schema; } + /** + * Build LocalBusiness schema from plugin parameters. + * + * @param object $params Plugin parameters object + * + * @return array|null + */ + public static function buildLocalBusiness(object $params): ?array + { + $name = trim((string) $params->get('lb_name', '')); + + if ($name === '') { + return null; + } + + $schema = [ + '@context' => 'https://schema.org', + '@type' => $params->get('lb_type', 'LocalBusiness'), + 'name' => $name, + ]; + + // Build PostalAddress + $address = []; + $street = trim((string) $params->get('lb_street', '')); + $city = trim((string) $params->get('lb_city', '')); + $region = trim((string) $params->get('lb_region', '')); + $postal = trim((string) $params->get('lb_postal', '')); + $country = trim((string) $params->get('lb_country', '')); + + if ($street !== '') { + $address['streetAddress'] = $street; + } + + if ($city !== '') { + $address['addressLocality'] = $city; + } + + if ($region !== '') { + $address['addressRegion'] = $region; + } + + if ($postal !== '') { + $address['postalCode'] = $postal; + } + + if ($country !== '') { + $address['addressCountry'] = $country; + } + + if (!empty($address)) { + $address['@type'] = 'PostalAddress'; + $schema['address'] = $address; + } + + // Contact properties + $phone = trim((string) $params->get('lb_phone', '')); + $email = trim((string) $params->get('lb_email', '')); + $url = trim((string) $params->get('lb_url', '')); + + if ($phone !== '') { + $schema['telephone'] = $phone; + } + + if ($email !== '') { + $schema['email'] = $email; + } + + if ($url !== '') { + $schema['url'] = $url; + } + + // Opening hours + $openingHours = trim((string) $params->get('lb_opening_hours', '')); + + if ($openingHours !== '') { + $schema['openingHours'] = $openingHours; + } + + // GeoCoordinates + $latitude = trim((string) $params->get('lb_latitude', '')); + $longitude = trim((string) $params->get('lb_longitude', '')); + + if ($latitude !== '' && $longitude !== '') { + $schema['geo'] = [ + '@type' => 'GeoCoordinates', + 'latitude' => $latitude, + 'longitude' => $longitude, + ]; + } + + // Price range + $priceRange = trim((string) $params->get('lb_price_range', '')); + + if ($priceRange !== '') { + $schema['priceRange'] = $priceRange; + } + + return $schema; + } + /** * Encode a schema array to a JSON-LD script tag string. * From 641eee753ad1dc5647834c08c75bf8feb799d386 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Tue, 23 Jun 2026 16:33:26 +0000 Subject: [PATCH 07/41] chore(version): auto-bump patch 01.03.08-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 2 +- README.md | 2 +- source/packages/com_mokoog/mokoog.xml | 2 +- source/packages/plg_content_mokoog/mokoog.xml | 2 +- source/packages/plg_system_mokoog/mokoog.xml | 2 +- source/packages/plg_webservices_mokoog/mokoog.xml | 2 +- source/pkg_mokoog.xml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 53040d4..53d2e35 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.03.07 +# VERSION: 01.03.08 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index e50c456..73dfa71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog - + All notable changes to MokoSuiteOpenGraph will be documented in this file. diff --git a/README.md b/README.md index 52eed3f..4f4ff89 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoSuiteOpenGraph - + Open Graph, Twitter Card, and social sharing meta tag management for Joomla 4/5/6. diff --git a/source/packages/com_mokoog/mokoog.xml b/source/packages/com_mokoog/mokoog.xml index ad61104..ade8ec2 100644 --- a/source/packages/com_mokoog/mokoog.xml +++ b/source/packages/com_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> com_mokoog - 01.03.07 + 01.03.08 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokoog/mokoog.xml b/source/packages/plg_content_mokoog/mokoog.xml index 688da2a..88cd3c3 100644 --- a/source/packages/plg_content_mokoog/mokoog.xml +++ b/source/packages/plg_content_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> Content - MokoJoomOpenGraph - 01.03.07 + 01.03.08 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokoog/mokoog.xml b/source/packages/plg_system_mokoog/mokoog.xml index d87c2a5..5e1b54e 100644 --- a/source/packages/plg_system_mokoog/mokoog.xml +++ b/source/packages/plg_system_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> System - MokoJoomOpenGraph - 01.03.07 + 01.03.08 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokoog/mokoog.xml b/source/packages/plg_webservices_mokoog/mokoog.xml index a70784a..c74774e 100644 --- a/source/packages/plg_webservices_mokoog/mokoog.xml +++ b/source/packages/plg_webservices_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> Web Services - MokoJoomOpenGraph - 01.03.07 + 01.03.08 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokoog.xml b/source/pkg_mokoog.xml index b55d457..0dbd69d 100644 --- a/source/pkg_mokoog.xml +++ b/source/pkg_mokoog.xml @@ -8,7 +8,7 @@ Package - MokoSuiteOpenGraph mokoog - 01.03.07 + 01.03.08 2026-05-23 Moko Consulting hello@mokoconsulting.tech From c871b7d30dd085c1b77023deb17335b5ec058558 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Tue, 23 Jun 2026 16:33:35 +0000 Subject: [PATCH 08/41] chore(version): pre-release bump to 01.04.01-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 2 +- README.md | 2 +- source/packages/com_mokoog/mokoog.xml | 2 +- source/packages/plg_content_mokoog/mokoog.xml | 2 +- source/packages/plg_system_mokoog/mokoog.xml | 2 +- source/packages/plg_webservices_mokoog/mokoog.xml | 2 +- source/pkg_mokoog.xml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 53d2e35..bd707ae 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.03.08 +# VERSION: 01.04.01 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index 73dfa71..19299db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog - + All notable changes to MokoSuiteOpenGraph will be documented in this file. diff --git a/README.md b/README.md index 4f4ff89..e352847 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoSuiteOpenGraph - + Open Graph, Twitter Card, and social sharing meta tag management for Joomla 4/5/6. diff --git a/source/packages/com_mokoog/mokoog.xml b/source/packages/com_mokoog/mokoog.xml index ade8ec2..e8c7d8a 100644 --- a/source/packages/com_mokoog/mokoog.xml +++ b/source/packages/com_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> com_mokoog - 01.03.08 + 01.04.01 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokoog/mokoog.xml b/source/packages/plg_content_mokoog/mokoog.xml index 88cd3c3..14bd592 100644 --- a/source/packages/plg_content_mokoog/mokoog.xml +++ b/source/packages/plg_content_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> Content - MokoJoomOpenGraph - 01.03.08 + 01.04.01 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokoog/mokoog.xml b/source/packages/plg_system_mokoog/mokoog.xml index 5e1b54e..07d9a89 100644 --- a/source/packages/plg_system_mokoog/mokoog.xml +++ b/source/packages/plg_system_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> System - MokoJoomOpenGraph - 01.03.08 + 01.04.01 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokoog/mokoog.xml b/source/packages/plg_webservices_mokoog/mokoog.xml index c74774e..81e85ed 100644 --- a/source/packages/plg_webservices_mokoog/mokoog.xml +++ b/source/packages/plg_webservices_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> Web Services - MokoJoomOpenGraph - 01.03.08 + 01.04.01 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokoog.xml b/source/pkg_mokoog.xml index 0dbd69d..661a2a9 100644 --- a/source/pkg_mokoog.xml +++ b/source/pkg_mokoog.xml @@ -8,7 +8,7 @@ Package - MokoSuiteOpenGraph mokoog - 01.03.08 + 01.04.01 2026-05-23 Moko Consulting hello@mokoconsulting.tech From 872074cd5b58e9ddbd21d34dac495edefa3785b4 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 23 Jun 2026 12:19:08 -0500 Subject: [PATCH 09/41] feat: add FAQ, HowTo, Event, and Recipe JSON-LD schema types - FAQ: auto-detects h3/h4 + paragraph patterns, outputs FAQPage (#62) - HowTo: auto-detects ordered lists, outputs HowTo with steps (#63) - Event: per-article fields (dates, venue, tickets), event_data JSON column, outputs Event schema (#64) - Recipe: per-article fields (times, ingredients, nutrition), recipe_data JSON column, outputs Recipe schema (#66) - DB migration 01.04.00: adds event_data and recipe_data columns Closes #62, closes #63, closes #64, closes #66 --- .../packages/com_mokoog/sql/install.mysql.sql | 2 + .../com_mokoog/sql/updates/mysql/01.04.00.sql | 6 + .../plg_content_mokoog/forms/mokoog.xml | 20 ++ .../language/en-GB/plg_content_mokoog.ini | 34 +++ .../language/en-US/plg_content_mokoog.ini | 34 +++ .../src/Extension/MokoOGContent.php | 45 ++- .../language/en-GB/plg_system_mokoog.ini | 4 + .../language/en-US/plg_system_mokoog.ini | 4 + source/packages/plg_system_mokoog/mokoog.xml | 22 ++ .../src/Extension/MokoOG.php | 149 ++++++++++ .../src/Helper/JsonLdBuilder.php | 273 ++++++++++++++++++ 11 files changed, 592 insertions(+), 1 deletion(-) create mode 100644 source/packages/com_mokoog/sql/updates/mysql/01.04.00.sql diff --git a/source/packages/com_mokoog/sql/install.mysql.sql b/source/packages/com_mokoog/sql/install.mysql.sql index 1bce7f2..86e2c45 100644 --- a/source/packages/com_mokoog/sql/install.mysql.sql +++ b/source/packages/com_mokoog/sql/install.mysql.sql @@ -13,6 +13,8 @@ CREATE TABLE IF NOT EXISTS `#__mokoog_tags` ( `og_image` VARCHAR(512) NOT NULL DEFAULT '', `og_type` VARCHAR(50) NOT NULL DEFAULT 'article', `og_video` VARCHAR(512) NOT NULL DEFAULT '', + `event_data` TEXT NULL, + `recipe_data` TEXT NULL, `seo_title` VARCHAR(70) NOT NULL DEFAULT '', `meta_description` VARCHAR(200) NOT NULL DEFAULT '', `robots` VARCHAR(100) NOT NULL DEFAULT '', diff --git a/source/packages/com_mokoog/sql/updates/mysql/01.04.00.sql b/source/packages/com_mokoog/sql/updates/mysql/01.04.00.sql new file mode 100644 index 0000000..225089e --- /dev/null +++ b/source/packages/com_mokoog/sql/updates/mysql/01.04.00.sql @@ -0,0 +1,6 @@ +-- +-- MokoJoomOpenGraph 01.04.00 - Add event_data and recipe_data columns +-- + +ALTER TABLE `#__mokoog_tags` ADD COLUMN `event_data` TEXT NULL AFTER `og_video`; +ALTER TABLE `#__mokoog_tags` ADD COLUMN `recipe_data` TEXT NULL AFTER `event_data`; diff --git a/source/packages/plg_content_mokoog/forms/mokoog.xml b/source/packages/plg_content_mokoog/forms/mokoog.xml index b5168e3..08635ce 100644 --- a/source/packages/plg_content_mokoog/forms/mokoog.xml +++ b/source/packages/plg_content_mokoog/forms/mokoog.xml @@ -101,5 +101,25 @@ validate="url" /> +
+ + + + + + + +
+
+ + + + + + + +
diff --git a/source/packages/plg_content_mokoog/language/en-GB/plg_content_mokoog.ini b/source/packages/plg_content_mokoog/language/en-GB/plg_content_mokoog.ini index e5859fa..85fe8f2 100644 --- a/source/packages/plg_content_mokoog/language/en-GB/plg_content_mokoog.ini +++ b/source/packages/plg_content_mokoog/language/en-GB/plg_content_mokoog.ini @@ -29,3 +29,37 @@ PLG_CONTENT_MOKOOG_FIELD_ROBOTS_DESC="Search engine indexing directives for this PLG_CONTENT_MOKOOG_ROBOTS_DEFAULT="- Use default (index, follow) -" PLG_CONTENT_MOKOOG_FIELD_CANONICAL_URL="Canonical URL" PLG_CONTENT_MOKOOG_FIELD_CANONICAL_URL_DESC="Override the canonical URL for this page. Leave blank to use the current URL." + +PLG_CONTENT_MOKOOG_FIELDSET_EVENT_LABEL="Event Details" +PLG_CONTENT_MOKOOG_FIELDSET_EVENT_DESC="Optional event information for JSON-LD Event schema." +PLG_CONTENT_MOKOOG_FIELD_EVENT_START="Start Date/Time" +PLG_CONTENT_MOKOOG_FIELD_EVENT_START_DESC="Event start date and time." +PLG_CONTENT_MOKOOG_FIELD_EVENT_END="End Date/Time" +PLG_CONTENT_MOKOOG_FIELD_EVENT_END_DESC="Event end date and time." +PLG_CONTENT_MOKOOG_FIELD_EVENT_LOCATION="Venue Name" +PLG_CONTENT_MOKOOG_FIELD_EVENT_LOCATION_DESC="Name of the event venue or location." +PLG_CONTENT_MOKOOG_FIELD_EVENT_ADDRESS="Venue Address" +PLG_CONTENT_MOKOOG_FIELD_EVENT_ADDRESS_DESC="Full address of the event venue." +PLG_CONTENT_MOKOOG_FIELD_EVENT_PRICE="Ticket Price" +PLG_CONTENT_MOKOOG_FIELD_EVENT_PRICE_DESC="Ticket price (e.g. 50.00). Leave blank if free." +PLG_CONTENT_MOKOOG_FIELD_EVENT_CURRENCY="Currency" +PLG_CONTENT_MOKOOG_FIELD_EVENT_CURRENCY_DESC="Currency code for ticket price (e.g. USD, EUR, GBP)." +PLG_CONTENT_MOKOOG_FIELD_EVENT_URL="Ticket URL" +PLG_CONTENT_MOKOOG_FIELD_EVENT_URL_DESC="URL where tickets can be purchased." + +PLG_CONTENT_MOKOOG_FIELDSET_RECIPE_LABEL="Recipe Details" +PLG_CONTENT_MOKOOG_FIELDSET_RECIPE_DESC="Optional recipe information for JSON-LD Recipe schema." +PLG_CONTENT_MOKOOG_FIELD_RECIPE_PREP_TIME="Prep Time" +PLG_CONTENT_MOKOOG_FIELD_RECIPE_PREP_TIME_DESC="Preparation time in ISO 8601 duration format (e.g. PT15M for 15 minutes)." +PLG_CONTENT_MOKOOG_FIELD_RECIPE_COOK_TIME="Cook Time" +PLG_CONTENT_MOKOOG_FIELD_RECIPE_COOK_TIME_DESC="Cooking time in ISO 8601 duration format (e.g. PT30M for 30 minutes)." +PLG_CONTENT_MOKOOG_FIELD_RECIPE_YIELD="Yield" +PLG_CONTENT_MOKOOG_FIELD_RECIPE_YIELD_DESC="Number of servings or yield (e.g. 4 servings, 1 loaf)." +PLG_CONTENT_MOKOOG_FIELD_RECIPE_CALORIES="Calories" +PLG_CONTENT_MOKOOG_FIELD_RECIPE_CALORIES_DESC="Calories per serving." +PLG_CONTENT_MOKOOG_FIELD_RECIPE_INGREDIENTS="Ingredients" +PLG_CONTENT_MOKOOG_FIELD_RECIPE_INGREDIENTS_DESC="One ingredient per line." +PLG_CONTENT_MOKOOG_FIELD_RECIPE_CATEGORY="Recipe Category" +PLG_CONTENT_MOKOOG_FIELD_RECIPE_CATEGORY_DESC="Category (e.g. Dessert, Appetizer, Main course)." +PLG_CONTENT_MOKOOG_FIELD_RECIPE_CUISINE="Cuisine" +PLG_CONTENT_MOKOOG_FIELD_RECIPE_CUISINE_DESC="Type of cuisine (e.g. Italian, Mexican, American)." diff --git a/source/packages/plg_content_mokoog/language/en-US/plg_content_mokoog.ini b/source/packages/plg_content_mokoog/language/en-US/plg_content_mokoog.ini index e5859fa..9a7634a 100644 --- a/source/packages/plg_content_mokoog/language/en-US/plg_content_mokoog.ini +++ b/source/packages/plg_content_mokoog/language/en-US/plg_content_mokoog.ini @@ -29,3 +29,37 @@ PLG_CONTENT_MOKOOG_FIELD_ROBOTS_DESC="Search engine indexing directives for this PLG_CONTENT_MOKOOG_ROBOTS_DEFAULT="- Use default (index, follow) -" PLG_CONTENT_MOKOOG_FIELD_CANONICAL_URL="Canonical URL" PLG_CONTENT_MOKOOG_FIELD_CANONICAL_URL_DESC="Override the canonical URL for this page. Leave blank to use the current URL." + +PLG_CONTENT_MOKOOG_FIELDSET_EVENT_LABEL="Event Details" +PLG_CONTENT_MOKOOG_FIELDSET_EVENT_DESC="Optional event information for JSON-LD Event schema." +PLG_CONTENT_MOKOOG_FIELD_EVENT_START="Start Date/Time" +PLG_CONTENT_MOKOOG_FIELD_EVENT_START_DESC="Event start date and time." +PLG_CONTENT_MOKOOG_FIELD_EVENT_END="End Date/Time" +PLG_CONTENT_MOKOOG_FIELD_EVENT_END_DESC="Event end date and time." +PLG_CONTENT_MOKOOG_FIELD_EVENT_LOCATION="Venue Name" +PLG_CONTENT_MOKOOG_FIELD_EVENT_LOCATION_DESC="Name of the event venue or location." +PLG_CONTENT_MOKOOG_FIELD_EVENT_ADDRESS="Venue Address" +PLG_CONTENT_MOKOOG_FIELD_EVENT_ADDRESS_DESC="Full address of the event venue." +PLG_CONTENT_MOKOOG_FIELD_EVENT_PRICE="Ticket Price" +PLG_CONTENT_MOKOOG_FIELD_EVENT_PRICE_DESC="Ticket price (e.g. 50.00). Leave blank if free." +PLG_CONTENT_MOKOOG_FIELD_EVENT_CURRENCY="Currency" +PLG_CONTENT_MOKOOG_FIELD_EVENT_CURRENCY_DESC="Currency code for ticket price (e.g. USD, EUR, GBP)." +PLG_CONTENT_MOKOOG_FIELD_EVENT_URL="Ticket URL" +PLG_CONTENT_MOKOOG_FIELD_EVENT_URL_DESC="URL where tickets can be purchased." + +PLG_CONTENT_MOKOOG_FIELDSET_RECIPE_LABEL="Recipe Details" +PLG_CONTENT_MOKOOG_FIELDSET_RECIPE_DESC="Optional recipe information for JSON-LD Recipe schema." +PLG_CONTENT_MOKOOG_FIELD_RECIPE_PREP_TIME="Prep Time" +PLG_CONTENT_MOKOOG_FIELD_RECIPE_PREP_TIME_DESC="Preparation time in ISO 8601 duration format (e.g. PT15M for 15 minutes)." +PLG_CONTENT_MOKOOG_FIELD_RECIPE_COOK_TIME="Cook Time" +PLG_CONTENT_MOKOOG_FIELD_RECIPE_COOK_TIME_DESC="Cooking time in ISO 8601 duration format (e.g. PT30M for 30 minutes)." +PLG_CONTENT_MOKOOG_FIELD_RECIPE_YIELD="Yield" +PLG_CONTENT_MOKOOG_FIELD_RECIPE_YIELD_DESC="Number of servings or yield (e.g. 4 servings, 1 loaf)." +PLG_CONTENT_MOKOOG_FIELD_RECIPE_CALORIES="Calories" +PLG_CONTENT_MOKOOG_FIELD_RECIPE_CALORIES_DESC="Calories per serving." +PLG_CONTENT_MOKOOG_FIELD_RECIPE_INGREDIENTS="Ingredients" +PLG_CONTENT_MOKOOG_FIELD_RECIPE_INGREDIENTS_DESC="One ingredient per line." +PLG_CONTENT_MOKOOG_FIELD_RECIPE_CATEGORY="Recipe Category" +PLG_CONTENT_MOKOOG_FIELD_RECIPE_CATEGORY_DESC="Category (e.g. Dessert, Appetizer, Main course)." +PLG_CONTENT_MOKOOG_FIELD_RECIPE_CUISINE="Cuisine" +PLG_CONTENT_MKOOG_FIELD_RECIPE_CUISINE_DESC="Type of cuisine (e.g. Italian, Mexican, American)." diff --git a/source/packages/plg_content_mokoog/src/Extension/MokoOGContent.php b/source/packages/plg_content_mokoog/src/Extension/MokoOGContent.php index b7bac0a..9d93f0e 100644 --- a/source/packages/plg_content_mokoog/src/Extension/MokoOGContent.php +++ b/source/packages/plg_content_mokoog/src/Extension/MokoOGContent.php @@ -98,7 +98,24 @@ final class MokoOGContent extends CMSPlugin implements SubscriberInterface $ogData = $this->loadOgData($contentType, $id, $language); if ($ogData) { - $form->bind(['mokoog' => (array) $ogData]); + $bindData = (array) $ogData; + + // Unpack JSON blob fields into individual form fields + foreach (['event_data', 'recipe_data'] as $jsonField) { + if (!empty($bindData[$jsonField])) { + $decoded = json_decode($bindData[$jsonField], true); + + if (\is_array($decoded)) { + foreach ($decoded as $key => $value) { + $bindData[$key] = $value; + } + } + } + + unset($bindData[$jsonField]); + } + + $form->bind(['mokoog' => $bindData]); } } } @@ -195,6 +212,7 @@ final class MokoOGContent extends CMSPlugin implements SubscriberInterface $query = $db->getQuery(true) ->select($db->quoteName([ 'og_title', 'og_description', 'og_image', 'og_type', 'og_video', + 'event_data', 'recipe_data', 'seo_title', 'meta_description', 'robots', 'canonical_url', ])) ->from($db->quoteName('#__mokoog_tags')) @@ -250,6 +268,8 @@ final class MokoOGContent extends CMSPlugin implements SubscriberInterface 'og_image' => trim($ogData['og_image'] ?? ''), 'og_type' => trim($ogData['og_type'] ?? 'article'), 'og_video' => $this->sanitizeUrl($ogData['og_video'] ?? ''), + 'event_data' => $this->packJsonFields($ogData, ['event_start', 'event_end', 'event_location', 'event_address', 'event_price', 'event_currency', 'event_url']), + 'recipe_data' => $this->packJsonFields($ogData, ['recipe_prep_time', 'recipe_cook_time', 'recipe_yield', 'recipe_calories', 'recipe_ingredients', 'recipe_category', 'recipe_cuisine']), 'seo_title' => strip_tags(trim($ogData['seo_title'] ?? '')), 'meta_description' => strip_tags(trim($ogData['meta_description'] ?? '')), 'robots' => trim($robots), @@ -267,6 +287,29 @@ final class MokoOGContent extends CMSPlugin implements SubscriberInterface } } + /** + * Pack form fields into a JSON string for storage. + * + * @param array $ogData Form data array + * @param array $fields Field names to pack + * + * @return string JSON string or empty + */ + private function packJsonFields(array $ogData, array $fields): string + { + $data = []; + + foreach ($fields as $field) { + $val = trim($ogData[$field] ?? ''); + + if ($val !== '') { + $data[$field] = $val; + } + } + + return !empty($data) ? json_encode($data) : ''; + } + /** * Sanitize a URL to only allow http/https schemes. * 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 2c8029d..b6df7cb 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 @@ -37,6 +37,10 @@ PLG_SYSTEM_MOKOOG_FIELD_AUTO_RESIZE="Auto-resize Images" PLG_SYSTEM_MOKOOG_FIELD_AUTO_RESIZE_DESC="Automatically resize images to 1200x630px (Facebook recommended) using center crop. Generated images are saved to images/mokoog/generated/." PLG_SYSTEM_MOKOOG_FIELD_JSONLD_ENABLED="Enable JSON-LD" PLG_SYSTEM_MOKOOG_FIELD_JSONLD_ENABLED_DESC="Output JSON-LD structured data (Article, WebPage) for Google rich results." +PLG_SYSTEM_MOKOOG_FIELD_JSONLD_FAQ="JSON-LD FAQ Schema" +PLG_SYSTEM_MOKOOG_FIELD_JSONLD_FAQ_DESC="Auto-detect FAQ sections from article headings (h3/h4 + paragraphs) and output FAQPage structured data." +PLG_SYSTEM_MOKOOG_FIELD_JSONLD_HOWTO="JSON-LD HowTo Schema" +PLG_SYSTEM_MOKOOG_FIELD_JSONLD_HOWTO_DESC="Auto-detect step-by-step instructions from ordered lists (ol/li) and output HowTo structured data." PLG_SYSTEM_MOKOOG_FIELD_JSONLD_BREADCRUMBS="JSON-LD Breadcrumbs" PLG_SYSTEM_MOKOOG_FIELD_JSONLD_BREADCRUMBS_DESC="Output BreadcrumbList JSON-LD schema from Joomla's pathway." 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 2c8029d..b6df7cb 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 @@ -37,6 +37,10 @@ PLG_SYSTEM_MOKOOG_FIELD_AUTO_RESIZE="Auto-resize Images" PLG_SYSTEM_MOKOOG_FIELD_AUTO_RESIZE_DESC="Automatically resize images to 1200x630px (Facebook recommended) using center crop. Generated images are saved to images/mokoog/generated/." PLG_SYSTEM_MOKOOG_FIELD_JSONLD_ENABLED="Enable JSON-LD" PLG_SYSTEM_MOKOOG_FIELD_JSONLD_ENABLED_DESC="Output JSON-LD structured data (Article, WebPage) for Google rich results." +PLG_SYSTEM_MOKOOG_FIELD_JSONLD_FAQ="JSON-LD FAQ Schema" +PLG_SYSTEM_MOKOOG_FIELD_JSONLD_FAQ_DESC="Auto-detect FAQ sections from article headings (h3/h4 + paragraphs) and output FAQPage structured data." +PLG_SYSTEM_MOKOOG_FIELD_JSONLD_HOWTO="JSON-LD HowTo Schema" +PLG_SYSTEM_MOKOOG_FIELD_JSONLD_HOWTO_DESC="Auto-detect step-by-step instructions from ordered lists (ol/li) and output HowTo structured data." PLG_SYSTEM_MOKOOG_FIELD_JSONLD_BREADCRUMBS="JSON-LD Breadcrumbs" PLG_SYSTEM_MOKOOG_FIELD_JSONLD_BREADCRUMBS_DESC="Output BreadcrumbList JSON-LD schema from Joomla's pathway." diff --git a/source/packages/plg_system_mokoog/mokoog.xml b/source/packages/plg_system_mokoog/mokoog.xml index 07d9a89..5a5d246 100644 --- a/source/packages/plg_system_mokoog/mokoog.xml +++ b/source/packages/plg_system_mokoog/mokoog.xml @@ -169,6 +169,28 @@ + + + + + + + + params->get('jsonld_faq', 1) && $option === 'com_content' && $view === 'article' && $id > 0) { + $faqItems = $this->extractFaqFromContent($id); + + if (!empty($faqItems)) { + $faqSchema = JsonLdBuilder::buildFaq($faqItems); + + if ($faqSchema) { + $doc->addCustomTag(JsonLdBuilder::toScriptTag($faqSchema)); + } + } + } + + // HowTo schema (auto-detected from ordered lists) + if ($this->params->get('jsonld_howto', 1) && $option === 'com_content' && $view === 'article' && $id > 0) { + $howToSteps = $this->extractHowToFromContent($id); + + if (!empty($howToSteps)) { + $howToSchema = JsonLdBuilder::buildHowTo($title, $howToSteps, $imageUrl); + + if ($howToSchema) { + $doc->addCustomTag(JsonLdBuilder::toScriptTag($howToSchema)); + } + } + } + + // Event JSON-LD from per-article event data + $eventJson = $ogData->event_data ?? ''; + + if (!empty($eventJson)) { + $eventObj = json_decode($eventJson); + + if ($eventObj && !empty($eventObj->event_start)) { + $eventSchema = JsonLdBuilder::buildEvent($title, $description, $imageUrl, $eventObj); + + if ($eventSchema) { + $doc->addCustomTag(JsonLdBuilder::toScriptTag($eventSchema)); + } + } + } + + // Recipe JSON-LD from per-article recipe data + $recipeJson = $ogData->recipe_data ?? ''; + + if (!empty($recipeJson)) { + $recipeObj = json_decode($recipeJson); + + if ($recipeObj) { + $recipeSchema = JsonLdBuilder::buildRecipe($title, $description, $imageUrl, $recipeObj); + + if ($recipeSchema) { + $doc->addCustomTag(JsonLdBuilder::toScriptTag($recipeSchema)); + } + } + } + if ($this->params->get('jsonld_breadcrumbs', 1)) { $breadcrumbs = JsonLdBuilder::buildBreadcrumbs(); @@ -370,6 +426,8 @@ final class MokoOG extends CMSPlugin implements SubscriberInterface 'og_image' => '', 'og_type' => '', 'og_video' => '', + 'event_data' => '', + 'recipe_data' => '', 'seo_title' => '', 'meta_description' => '', 'robots' => '', @@ -640,6 +698,97 @@ final class MokoOG extends CMSPlugin implements SubscriberInterface return $article->author_name ?? ''; } + /** + * Extract FAQ question/answer pairs from article content. + * + * @param int $articleId Article ID + * + * @return array Array of ['question' => '...', 'answer' => '...'] pairs + */ + private function extractFaqFromContent(int $articleId): array + { + $article = $this->loadArticle($articleId); + + if (!$article) { + return []; + } + + $content = ($article->introtext ?? '') . ($article->fulltext ?? ''); + + if (trim($content) === '') { + return []; + } + + $faqItems = []; + + if (preg_match_all('/]*>(.*?)<\/h[34]>\s*((?:]*>.*?<\/p>\s*)+)/si', $content, $matches, PREG_SET_ORDER)) { + foreach ($matches as $match) { + $question = trim(strip_tags($match[1])); + $answer = trim(strip_tags($match[2])); + + if ($question !== '' && $answer !== '') { + $faqItems[] = [ + 'question' => $question, + 'answer' => $answer, + ]; + } + } + } + + return $faqItems; + } + + /** + * Extract HowTo steps from ordered lists in article content. + * + * @param int $articleId Article ID + * + * @return array Array of ['name' => '...', 'text' => '...'] pairs + */ + private function extractHowToFromContent(int $articleId): array + { + $article = $this->loadArticle($articleId); + + if (!$article) { + return []; + } + + $content = ($article->introtext ?? '') . ($article->fulltext ?? ''); + + if (!preg_match('/]*>(.*?)<\/ol>/si', $content, $olMatch)) { + return []; + } + + if (!preg_match_all('/]*>(.*?)<\/li>/si', $olMatch[1], $liMatches)) { + return []; + } + + $steps = []; + + foreach ($liMatches[1] as $liHtml) { + $text = trim(strip_tags($liHtml)); + + if ($text === '') { + continue; + } + + $name = $text; + + if (preg_match('/<(?:b|strong)[^>]*>(.*?)<\/(?:b|strong)>/si', $liHtml, $boldMatch)) { + $name = trim(strip_tags($boldMatch[1])); + } elseif (preg_match('/^([^.!?]+[.!?])/', $text, $sentenceMatch)) { + $name = trim($sentenceMatch[1]); + } + + $steps[] = [ + 'name' => $name, + 'text' => $text, + ]; + } + + return $steps; + } + /** * Warn administrators once per session when no license key is configured. * diff --git a/source/packages/plg_system_mokoog/src/Helper/JsonLdBuilder.php b/source/packages/plg_system_mokoog/src/Helper/JsonLdBuilder.php index 649bf07..5a84e6e 100644 --- a/source/packages/plg_system_mokoog/src/Helper/JsonLdBuilder.php +++ b/source/packages/plg_system_mokoog/src/Helper/JsonLdBuilder.php @@ -382,6 +382,279 @@ class JsonLdBuilder return $schema; } + /** + * Build FAQPage schema from question/answer pairs. + * + * @param array $questions Array of ['question' => '...', 'answer' => '...'] pairs + * + * @return array|null + */ + public static function buildFaq(array $questions): ?array + { + if (empty($questions)) { + return null; + } + + $mainEntity = []; + + foreach ($questions as $item) { + $question = trim($item['question'] ?? ''); + $answer = trim($item['answer'] ?? ''); + + if ($question === '' || $answer === '') { + continue; + } + + $mainEntity[] = [ + '@type' => 'Question', + 'name' => $question, + 'acceptedAnswer' => [ + '@type' => 'Answer', + 'text' => $answer, + ], + ]; + } + + if (empty($mainEntity)) { + return null; + } + + return [ + '@context' => 'https://schema.org', + '@type' => 'FAQPage', + 'mainEntity' => $mainEntity, + ]; + } + + /** + * Build HowTo schema from step-by-step instructions. + * + * @param string $title HowTo title + * @param array $steps Array of ['name' => 'Step title', 'text' => 'Step instructions'] + * @param string $imageUrl Optional image URL (absolute) + * + * @return array|null + */ + public static function buildHowTo(string $title, array $steps, string $imageUrl = ''): ?array + { + if (empty($steps)) { + return null; + } + + $schema = [ + '@context' => 'https://schema.org', + '@type' => 'HowTo', + 'name' => $title, + ]; + + if (!empty($imageUrl)) { + $schema['image'] = $imageUrl; + } + + $schema['step'] = []; + + foreach ($steps as $step) { + $schema['step'][] = [ + '@type' => 'HowToStep', + 'name' => $step['name'], + 'text' => $step['text'], + ]; + } + + return $schema; + } + + /** + * Build Event schema from per-article event data. + * + * @param string $title Event/article title + * @param string $description Event description + * @param string $imageUrl Image URL (absolute) + * @param object $eventData Decoded event_data with event_start, event_end, etc. + * + * @return array|null + */ + public static function buildEvent(string $title, string $description, string $imageUrl, object $eventData): ?array + { + $startDate = $eventData->event_start ?? ''; + + if (empty($startDate)) { + return null; + } + + $schema = [ + '@context' => 'https://schema.org', + '@type' => 'Event', + 'name' => $title, + 'description' => $description, + 'startDate' => $startDate, + 'url' => Uri::getInstance()->toString(), + ]; + + $endDate = $eventData->event_end ?? ''; + + if (!empty($endDate)) { + $schema['endDate'] = $endDate; + } + + if (!empty($imageUrl)) { + $schema['image'] = $imageUrl; + } + + $locationName = $eventData->event_location ?? ''; + $address = $eventData->event_address ?? ''; + + if (!empty($locationName) || !empty($address)) { + $location = ['@type' => 'Place']; + + if (!empty($locationName)) { + $location['name'] = $locationName; + } + + if (!empty($address)) { + $location['address'] = [ + '@type' => 'PostalAddress', + 'streetAddress' => $address, + ]; + } + + $schema['location'] = $location; + } + + $price = $eventData->event_price ?? ''; + $currency = $eventData->event_currency ?? 'USD'; + $ticketUrl = $eventData->event_url ?? ''; + + if ($price !== '') { + $offer = [ + '@type' => 'Offer', + 'price' => number_format((float) $price, 2, '.', ''), + 'priceCurrency' => $currency ?: 'USD', + 'availability' => 'https://schema.org/InStock', + ]; + + if (!empty($ticketUrl)) { + $offer['url'] = $ticketUrl; + } + + $schema['offers'] = $offer; + } elseif (!empty($ticketUrl)) { + $schema['offers'] = [ + '@type' => 'Offer', + 'price' => '0.00', + 'priceCurrency' => $currency ?: 'USD', + 'availability' => 'https://schema.org/InStock', + 'url' => $ticketUrl, + ]; + } + + return $schema; + } + + /** + * Build Recipe schema from per-article recipe data. + * + * @param string $title Recipe/article title + * @param string $description Recipe/article description + * @param string $imageUrl Image URL (absolute) + * @param object $recipeData Decoded recipe_data object + * + * @return array|null + */ + public static function buildRecipe(string $title, string $description, string $imageUrl, object $recipeData): ?array + { + $fields = ['recipe_prep_time', 'recipe_cook_time', 'recipe_yield', 'recipe_calories', 'recipe_ingredients', 'recipe_category', 'recipe_cuisine']; + $hasData = false; + + foreach ($fields as $field) { + if (!empty($recipeData->$field)) { + $hasData = true; + break; + } + } + + if (!$hasData) { + return null; + } + + $schema = [ + '@context' => 'https://schema.org', + '@type' => 'Recipe', + 'name' => $title, + 'description' => $description, + 'url' => Uri::getInstance()->toString(), + ]; + + if (!empty($imageUrl)) { + $schema['image'] = $imageUrl; + } + + if (!empty($recipeData->recipe_prep_time)) { + $schema['prepTime'] = $recipeData->recipe_prep_time; + } + + if (!empty($recipeData->recipe_cook_time)) { + $schema['cookTime'] = $recipeData->recipe_cook_time; + } + + if (!empty($recipeData->recipe_prep_time) && !empty($recipeData->recipe_cook_time)) { + try { + $prep = new \DateInterval($recipeData->recipe_prep_time); + $cook = new \DateInterval($recipeData->recipe_cook_time); + $totalMinutes = ($prep->h * 60 + $prep->i) + ($cook->h * 60 + $cook->i); + $hours = intdiv($totalMinutes, 60); + $minutes = $totalMinutes % 60; + $totalTime = 'PT'; + + if ($hours > 0) { + $totalTime .= $hours . 'H'; + } + + if ($minutes > 0) { + $totalTime .= $minutes . 'M'; + } + + if ($totalTime !== 'PT') { + $schema['totalTime'] = $totalTime; + } + } catch (\Exception $e) { + // Invalid duration format + } + } + + if (!empty($recipeData->recipe_yield)) { + $schema['recipeYield'] = $recipeData->recipe_yield; + } + + if (!empty($recipeData->recipe_calories)) { + $schema['nutrition'] = [ + '@type' => 'NutritionInformation', + 'calories' => $recipeData->recipe_calories . ' calories', + ]; + } + + if (!empty($recipeData->recipe_ingredients)) { + $ingredients = array_filter( + array_map('trim', preg_split('/\r\n|\r|\n/', $recipeData->recipe_ingredients)), + fn($line) => $line !== '' + ); + + if (!empty($ingredients)) { + $schema['recipeIngredient'] = array_values($ingredients); + } + } + + if (!empty($recipeData->recipe_category)) { + $schema['recipeCategory'] = $recipeData->recipe_category; + } + + if (!empty($recipeData->recipe_cuisine)) { + $schema['recipeCuisine'] = $recipeData->recipe_cuisine; + } + + return $schema; + } + /** * Encode a schema array to a JSON-LD script tag string. * From e530ca821ee2155dfc784872b6ad99b7e4de3b37 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Tue, 23 Jun 2026 17:19:49 +0000 Subject: [PATCH 10/41] chore(version): auto-bump patch 01.04.02-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 2 +- README.md | 2 +- source/packages/com_mokoog/mokoog.xml | 2 +- source/packages/plg_content_mokoog/mokoog.xml | 2 +- source/packages/plg_system_mokoog/mokoog.xml | 2 +- source/packages/plg_webservices_mokoog/mokoog.xml | 2 +- source/pkg_mokoog.xml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index bd707ae..8038885 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.04.01 +# VERSION: 01.04.02 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index 19299db..d97d7d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog - + All notable changes to MokoSuiteOpenGraph will be documented in this file. diff --git a/README.md b/README.md index e352847..c296321 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoSuiteOpenGraph - + Open Graph, Twitter Card, and social sharing meta tag management for Joomla 4/5/6. diff --git a/source/packages/com_mokoog/mokoog.xml b/source/packages/com_mokoog/mokoog.xml index e8c7d8a..53155d2 100644 --- a/source/packages/com_mokoog/mokoog.xml +++ b/source/packages/com_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> com_mokoog - 01.04.01 + 01.04.02 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokoog/mokoog.xml b/source/packages/plg_content_mokoog/mokoog.xml index 14bd592..5aee952 100644 --- a/source/packages/plg_content_mokoog/mokoog.xml +++ b/source/packages/plg_content_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> Content - MokoJoomOpenGraph - 01.04.01 + 01.04.02 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokoog/mokoog.xml b/source/packages/plg_system_mokoog/mokoog.xml index 5a5d246..411efa0 100644 --- a/source/packages/plg_system_mokoog/mokoog.xml +++ b/source/packages/plg_system_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> System - MokoJoomOpenGraph - 01.04.01 + 01.04.02 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokoog/mokoog.xml b/source/packages/plg_webservices_mokoog/mokoog.xml index 81e85ed..ce5012d 100644 --- a/source/packages/plg_webservices_mokoog/mokoog.xml +++ b/source/packages/plg_webservices_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> Web Services - MokoJoomOpenGraph - 01.04.01 + 01.04.02 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokoog.xml b/source/pkg_mokoog.xml index 661a2a9..b0582ea 100644 --- a/source/pkg_mokoog.xml +++ b/source/pkg_mokoog.xml @@ -8,7 +8,7 @@ Package - MokoSuiteOpenGraph mokoog - 01.04.01 + 01.04.02 2026-05-23 Moko Consulting hello@mokoconsulting.tech From 7a38025b5e8e8c8c14d9c788727df19ec9f686e3 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Tue, 23 Jun 2026 17:20:08 +0000 Subject: [PATCH 11/41] chore(version): pre-release bump to 01.04.03-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 2 +- README.md | 2 +- source/packages/com_mokoog/mokoog.xml | 2 +- source/packages/plg_content_mokoog/mokoog.xml | 2 +- source/packages/plg_system_mokoog/mokoog.xml | 2 +- source/packages/plg_webservices_mokoog/mokoog.xml | 2 +- source/pkg_mokoog.xml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 8038885..5de1024 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.04.02 +# VERSION: 01.04.03 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index d97d7d5..75e0dc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog - + All notable changes to MokoSuiteOpenGraph will be documented in this file. diff --git a/README.md b/README.md index c296321..b2f8ee2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoSuiteOpenGraph - + Open Graph, Twitter Card, and social sharing meta tag management for Joomla 4/5/6. diff --git a/source/packages/com_mokoog/mokoog.xml b/source/packages/com_mokoog/mokoog.xml index 53155d2..0503799 100644 --- a/source/packages/com_mokoog/mokoog.xml +++ b/source/packages/com_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> com_mokoog - 01.04.02 + 01.04.03 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_content_mokoog/mokoog.xml b/source/packages/plg_content_mokoog/mokoog.xml index 5aee952..bc876fe 100644 --- a/source/packages/plg_content_mokoog/mokoog.xml +++ b/source/packages/plg_content_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> Content - MokoJoomOpenGraph - 01.04.02 + 01.04.03 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokoog/mokoog.xml b/source/packages/plg_system_mokoog/mokoog.xml index 411efa0..54d956f 100644 --- a/source/packages/plg_system_mokoog/mokoog.xml +++ b/source/packages/plg_system_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> System - MokoJoomOpenGraph - 01.04.02 + 01.04.03 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokoog/mokoog.xml b/source/packages/plg_webservices_mokoog/mokoog.xml index ce5012d..8b05cff 100644 --- a/source/packages/plg_webservices_mokoog/mokoog.xml +++ b/source/packages/plg_webservices_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> Web Services - MokoJoomOpenGraph - 01.04.02 + 01.04.03 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokoog.xml b/source/pkg_mokoog.xml index b0582ea..03198ac 100644 --- a/source/pkg_mokoog.xml +++ b/source/pkg_mokoog.xml @@ -8,7 +8,7 @@ Package - MokoSuiteOpenGraph mokoog - 01.04.02 + 01.04.03 2026-05-23 Moko Consulting hello@mokoconsulting.tech From f649858fcd53b4918ef28838674bef175f25fc97 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 23 Jun 2026 12:29:17 -0500 Subject: [PATCH 12/41] feat: add SEO content scoring panel to article editor JavaScript-based SEO analysis with 7 checks (OG title, description, image, SEO title, meta description, title length, description length). Shows pass/fail dots and overall score. Closes #68 --- .../plg_content_mokoog/media/css/preview.css | 13 +++ .../plg_content_mokoog/media/js/preview.js | 79 ++++++++++++++++++- 2 files changed, 89 insertions(+), 3 deletions(-) diff --git a/source/packages/plg_content_mokoog/media/css/preview.css b/source/packages/plg_content_mokoog/media/css/preview.css index 7f7d915..346f8e8 100644 --- a/source/packages/plg_content_mokoog/media/css/preview.css +++ b/source/packages/plg_content_mokoog/media/css/preview.css @@ -240,3 +240,16 @@ color: #d32f2f; font-weight: 600; } + +/* SEO scoring panel */ +.mokoog-seo-score { margin: 15px 0; padding: 15px; background: #f8f9fa; border-radius: 8px; border: 1px solid #dee2e6; } +.mokoog-seo-heading { margin: 0 0 10px; font-size: 14px; color: #666; } +.mokoog-seo-list { list-style: none; padding: 0; margin: 0 0 10px; } +.mokoog-seo-item { padding: 4px 0; font-size: 13px; display: flex; align-items: center; gap: 8px; } +.mokoog-seo-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; } +.mokoog-seo-pass { background: #2e7d32; } +.mokoog-seo-fail { background: #d32f2f; } +.mokoog-seo-total { font-size: 14px; font-weight: 600; padding-top: 8px; border-top: 1px solid #dee2e6; } +.mokoog-seo-total-good { color: #2e7d32; } +.mokoog-seo-total-ok { color: #f57c00; } +.mokoog-seo-total-bad { color: #d32f2f; } diff --git a/source/packages/plg_content_mokoog/media/js/preview.js b/source/packages/plg_content_mokoog/media/js/preview.js index 8a17c70..77192b2 100644 --- a/source/packages/plg_content_mokoog/media/js/preview.js +++ b/source/packages/plg_content_mokoog/media/js/preview.js @@ -367,17 +367,90 @@ document.addEventListener('DOMContentLoaded', function () { document.getElementById('mokoog-sl-domain').textContent = domain; } + // SEO scoring panel + var seoChecks = [ + { id: 'og-title', label: 'OG Title', check: function() { return fields.ogTitle && fields.ogTitle.value.length > 0; }}, + { id: 'og-desc', label: 'OG Description', check: function() { return fields.ogDesc && fields.ogDesc.value.length > 0; }}, + { id: 'og-image', label: 'OG Image', check: function() { return fields.ogImage && fields.ogImage.value.length > 0; }}, + { id: 'seo-title', label: 'SEO Title', check: function() { return fields.seoTitle && fields.seoTitle.value.length > 0; }}, + { id: 'meta-desc', label: 'Meta Description', check: function() { return fields.metaDescription && fields.metaDescription.value.length > 0; }}, + { id: 'title-length', label: 'Title Length (\u226460)', check: function() { + var t = (fields.ogTitle && fields.ogTitle.value) || (fields.articleTitle && fields.articleTitle.value) || ''; + return t.length > 0 && t.length <= 60; + }}, + { id: 'desc-length', label: 'Description Length (\u2264160)', check: function() { + var d = (fields.ogDesc && fields.ogDesc.value) || (fields.metaDesc && fields.metaDesc.value) || ''; + return d.length > 0 && d.length <= 160; + }} + ]; + + var seoPanel = document.createElement('div'); + seoPanel.className = 'mokoog-seo-score'; + + var seoHeading = document.createElement('h4'); + seoHeading.className = 'mokoog-seo-heading'; + seoHeading.textContent = 'SEO Analysis'; + seoPanel.appendChild(seoHeading); + + var seoList = document.createElement('ul'); + seoList.className = 'mokoog-seo-list'; + + var seoDots = {}; + seoChecks.forEach(function (chk) { + var li = document.createElement('li'); + li.className = 'mokoog-seo-item'; + + var dot = document.createElement('span'); + dot.className = 'mokoog-seo-dot mokoog-seo-fail'; + seoDots[chk.id] = dot; + li.appendChild(dot); + + var label = document.createElement('span'); + label.textContent = chk.label; + li.appendChild(label); + + seoList.appendChild(li); + }); + + seoPanel.appendChild(seoList); + + var seoTotal = document.createElement('div'); + seoTotal.className = 'mokoog-seo-total'; + seoPanel.appendChild(seoTotal); + + wrapper.parentNode.insertBefore(seoPanel, wrapper.nextSibling); + + function updateSeoScore() { + var passed = 0; + seoChecks.forEach(function (chk) { + var ok = chk.check(); + if (ok) passed++; + seoDots[chk.id].className = 'mokoog-seo-dot ' + (ok ? 'mokoog-seo-pass' : 'mokoog-seo-fail'); + }); + + seoTotal.textContent = passed + '/' + seoChecks.length + ' checks passed'; + + if (passed === seoChecks.length) { + seoTotal.className = 'mokoog-seo-total mokoog-seo-total-good'; + } else if (passed >= Math.ceil(seoChecks.length / 2)) { + seoTotal.className = 'mokoog-seo-total mokoog-seo-total-ok'; + } else { + seoTotal.className = 'mokoog-seo-total mokoog-seo-total-bad'; + } + } + Object.values(fields).forEach(function (el) { if (el) { - el.addEventListener('input', updatePreview); - el.addEventListener('change', updatePreview); + el.addEventListener('input', function () { updatePreview(); updateSeoScore(); }); + el.addEventListener('change', function () { updatePreview(); updateSeoScore(); }); } }); if (fields.ogImage) { - var observer = new MutationObserver(updatePreview); + var observer = new MutationObserver(function () { updatePreview(); updateSeoScore(); }); observer.observe(fields.ogImage, { attributes: true, attributeFilter: ['value'] }); } updatePreview(); + updateSeoScore(); }); From 2088b3f13fe37a2a4ddbdb6f5d0c03ebcd72a0af Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 23 Jun 2026 12:29:38 -0500 Subject: [PATCH 13/41] feat: add OG coverage dashboard to tag manager Shows coverage percentage, article count with/without OG tags, and counts of missing title, description, and image fields. Closes #73 --- .../com_mokoog/language/en-GB/com_mokoog.ini | 7 +++ .../com_mokoog/language/en-US/com_mokoog.ini | 7 +++ .../com_mokoog/tmpl/tags/coverage.php | 58 +++++++++++++++++++ .../packages/com_mokoog/tmpl/tags/default.php | 1 + 4 files changed, 73 insertions(+) create mode 100644 source/packages/com_mokoog/tmpl/tags/coverage.php diff --git a/source/packages/com_mokoog/language/en-GB/com_mokoog.ini b/source/packages/com_mokoog/language/en-GB/com_mokoog.ini index e8db069..4e8f84a 100644 --- a/source/packages/com_mokoog/language/en-GB/com_mokoog.ini +++ b/source/packages/com_mokoog/language/en-GB/com_mokoog.ini @@ -59,3 +59,10 @@ COM_MOKOOG_IMPORT_INVALID_TYPE="Invalid file type. Please upload a .csv file." COM_MOKOOG_IMPORT_FILE_TOO_LARGE="File is too large. Maximum allowed size is %s." COM_MOKOOG_IMPORT_READ_ERROR="Could not read the uploaded CSV file." COM_MOKOOG_IMPORT_RESULT="Import complete: %d created, %d updated, %d skipped." + +COM_MOKOOG_COVERAGE_TITLE="OG Tag Coverage" +COM_MOKOOG_COVERAGE_PERCENT="OG Coverage" +COM_MOKOOG_COVERAGE_ARTICLES="%d of %d articles have OG tags" +COM_MOKOOG_COVERAGE_MISSING_TITLE="%d tags missing custom title" +COM_MOKOOG_COVERAGE_MISSING_DESC="%d tags missing custom description" +COM_MOKOOG_COVERAGE_MISSING_IMAGE="%d tags missing custom image" diff --git a/source/packages/com_mokoog/language/en-US/com_mokoog.ini b/source/packages/com_mokoog/language/en-US/com_mokoog.ini index e8db069..4e8f84a 100644 --- a/source/packages/com_mokoog/language/en-US/com_mokoog.ini +++ b/source/packages/com_mokoog/language/en-US/com_mokoog.ini @@ -59,3 +59,10 @@ COM_MOKOOG_IMPORT_INVALID_TYPE="Invalid file type. Please upload a .csv file." COM_MOKOOG_IMPORT_FILE_TOO_LARGE="File is too large. Maximum allowed size is %s." COM_MOKOOG_IMPORT_READ_ERROR="Could not read the uploaded CSV file." COM_MOKOOG_IMPORT_RESULT="Import complete: %d created, %d updated, %d skipped." + +COM_MOKOOG_COVERAGE_TITLE="OG Tag Coverage" +COM_MOKOOG_COVERAGE_PERCENT="OG Coverage" +COM_MOKOOG_COVERAGE_ARTICLES="%d of %d articles have OG tags" +COM_MOKOOG_COVERAGE_MISSING_TITLE="%d tags missing custom title" +COM_MOKOOG_COVERAGE_MISSING_DESC="%d tags missing custom description" +COM_MOKOOG_COVERAGE_MISSING_IMAGE="%d tags missing custom image" diff --git a/source/packages/com_mokoog/tmpl/tags/coverage.php b/source/packages/com_mokoog/tmpl/tags/coverage.php new file mode 100644 index 0000000..b2f0830 --- /dev/null +++ b/source/packages/com_mokoog/tmpl/tags/coverage.php @@ -0,0 +1,58 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; + +$db = Factory::getDbo(); + +// Total published articles +$db->setQuery($db->getQuery(true)->select('COUNT(*)')->from('#__content')->where('state = 1')); +$totalArticles = (int) $db->loadResult(); + +// Articles with OG tags +$db->setQuery($db->getQuery(true)->select('COUNT(DISTINCT content_id)')->from('#__mokoog_tags')->where("content_type = 'com_content'")->where('published = 1')); +$articlesWithOg = (int) $db->loadResult(); + +// Articles missing OG data fields +$db->setQuery($db->getQuery(true)->select('COUNT(*)')->from('#__mokoog_tags')->where("content_type = 'com_content'")->where("og_title = ''")->where('published = 1')); +$missingTitle = (int) $db->loadResult(); + +$db->setQuery($db->getQuery(true)->select('COUNT(*)')->from('#__mokoog_tags')->where("content_type = 'com_content'")->where("og_description = ''")->where('published = 1')); +$missingDesc = (int) $db->loadResult(); + +$db->setQuery($db->getQuery(true)->select('COUNT(*)')->from('#__mokoog_tags')->where("content_type = 'com_content'")->where("og_image = ''")->where('published = 1')); +$missingImage = (int) $db->loadResult(); + +$coverage = $totalArticles > 0 ? round(($articlesWithOg / $totalArticles) * 100) : 0; +?> +
+
+

+
+
+
+ % +
+ +
+
+
    +
  • +
  • +
  • +
  • +
+
+
+
+
diff --git a/source/packages/com_mokoog/tmpl/tags/default.php b/source/packages/com_mokoog/tmpl/tags/default.php index 6ca4e90..0367944 100644 --- a/source/packages/com_mokoog/tmpl/tags/default.php +++ b/source/packages/com_mokoog/tmpl/tags/default.php @@ -21,6 +21,7 @@ use Joomla\CMS\Session\Session; $token = Session::getFormToken(); ?> +
From e7b0af1fca5d9962b43cd9bdeed209b8c4633419 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 23 Jun 2026 12:30:18 -0500 Subject: [PATCH 14/41] docs: add OpenAPI 3.0 spec for REST API Documents the /api/v1/mokoog/tags endpoints with full request/response schemas, authentication, and examples. Closes #80 --- openapi.yaml | 668 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 668 insertions(+) create mode 100644 openapi.yaml diff --git a/openapi.yaml b/openapi.yaml new file mode 100644 index 0000000..c7508b5 --- /dev/null +++ b/openapi.yaml @@ -0,0 +1,668 @@ +openapi: 3.0.3 + +info: + title: MokoSuiteOpenGraph API + version: 1.0.0 + description: | + REST API for managing Open Graph, SEO meta, and structured-data tags in + Joomla via the MokoSuiteOpenGraph extension. + + The API follows Joomla's Web Services conventions and returns responses in + [JSON:API](https://jsonapi.org/) format. All endpoints require + authentication via a Joomla API token. + contact: + name: Moko Consulting + email: hello@mokoconsulting.tech + license: + name: GPL-3.0-or-later + url: https://www.gnu.org/licenses/gpl-3.0.html + +servers: + - url: /api/index.php/v1 + description: Joomla Web Services API + +security: + - apiToken: [] + +tags: + - name: Tags + description: CRUD operations for Open Graph tag records + +paths: + /mokoog/tags: + get: + operationId: listTags + summary: List OG tags + description: | + Returns a paginated collection of OG tag records. Supports filtering + by content type, published state, and language. + tags: [Tags] + parameters: + - name: "filter[content_type]" + in: query + description: Filter by content type (e.g. `com_content`, `menu`, `com_mokoshop`) + schema: + type: string + example: com_content + - name: "filter[content_id]" + in: query + description: Filter by content ID + schema: + type: integer + example: 42 + - name: "filter[published]" + in: query + description: Filter by published state + schema: + type: integer + enum: [0, 1] + - name: "filter[language]" + in: query + description: Filter by language tag (e.g. `en-GB`, `*`) + schema: + type: string + example: "*" + - name: "filter[search]" + in: query + description: Free-text search across tag fields + schema: + type: string + - name: "page[offset]" + in: query + description: Number of records to skip (pagination offset) + schema: + type: integer + minimum: 0 + default: 0 + - name: "page[limit]" + in: query + description: Maximum number of records to return + schema: + type: integer + minimum: 1 + maximum: 100 + default: 25 + - name: "list[fullordering]" + in: query + description: Sort order for results + schema: + type: string + enum: + - a.id ASC + - a.id DESC + - a.og_title ASC + - a.og_title DESC + - a.modified ASC + - a.modified DESC + default: a.modified DESC + responses: + "200": + description: A JSON:API collection of OG tags + content: + application/vnd.api+json: + schema: + $ref: "#/components/schemas/TagCollection" + example: + links: + self: "/api/index.php/v1/mokoog/tags" + data: + - type: tags + id: "1" + attributes: + content_type: com_content + content_id: 42 + og_title: "My Article Title" + og_description: "A brief description for social sharing." + og_image: "images/mokoog/og-banner.jpg" + og_type: article + seo_title: "My Article | Example Site" + meta_description: "A brief meta description for search engines." + robots: "index, follow" + canonical_url: "https://example.com/my-article" + language: "*" + published: 1 + created: "2026-06-01T12:00:00+00:00" + modified: "2026-06-15T08:30:00+00:00" + meta: + total-pages: 1 + "401": + $ref: "#/components/responses/Unauthorized" + + post: + operationId: createTag + summary: Create an OG tag + description: | + Creates a new OG tag record. The combination of `content_type`, + `content_id`, and `language` must be unique. + tags: [Tags] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/TagCreateRequest" + example: + content_type: com_content + content_id: 42 + og_title: "My Article Title" + og_description: "A brief description for social sharing." + og_image: "images/mokoog/og-banner.jpg" + og_type: article + language: "*" + published: 1 + responses: + "200": + description: The created tag + content: + application/vnd.api+json: + schema: + $ref: "#/components/schemas/TagDocument" + example: + links: + self: "/api/index.php/v1/mokoog/tags/1" + data: + type: tags + id: "1" + attributes: + content_type: com_content + content_id: 42 + og_title: "My Article Title" + og_description: "A brief description for social sharing." + og_image: "images/mokoog/og-banner.jpg" + og_type: article + seo_title: "" + meta_description: "" + robots: "" + canonical_url: "" + language: "*" + published: 1 + created: "2026-06-23T10:00:00+00:00" + modified: "2026-06-23T10:00:00+00:00" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + + /mokoog/tags/{id}: + parameters: + - $ref: "#/components/parameters/TagId" + + get: + operationId: getTag + summary: Get a single OG tag + tags: [Tags] + responses: + "200": + description: A single OG tag resource + content: + application/vnd.api+json: + schema: + $ref: "#/components/schemas/TagDocument" + example: + links: + self: "/api/index.php/v1/mokoog/tags/1" + data: + type: tags + id: "1" + attributes: + content_type: com_content + content_id: 42 + og_title: "My Article Title" + og_description: "A brief description for social sharing." + og_image: "images/mokoog/og-banner.jpg" + og_type: article + seo_title: "My Article | Example Site" + meta_description: "A brief meta description for search engines." + robots: "index, follow" + canonical_url: "https://example.com/my-article" + language: "*" + published: 1 + created: "2026-06-01T12:00:00+00:00" + modified: "2026-06-15T08:30:00+00:00" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + + patch: + operationId: updateTag + summary: Update an OG tag + description: Partially updates an existing OG tag. Only supplied fields are changed. + tags: [Tags] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/TagUpdateRequest" + example: + og_title: "Updated Title" + og_description: "Updated social description." + published: 0 + responses: + "200": + description: The updated tag + content: + application/vnd.api+json: + schema: + $ref: "#/components/schemas/TagDocument" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + + delete: + operationId: deleteTag + summary: Delete an OG tag + tags: [Tags] + responses: + "204": + description: Tag deleted successfully + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + + /mokoog/lookup/{content_type}/{content_id}: + get: + operationId: lookupTag + summary: Look up an OG tag by content type and content ID + description: | + Resolves an OG tag by its `content_type` and `content_id` pair and + returns the full tag resource. This is a convenience endpoint that + avoids the caller needing to know the internal tag ID. + tags: [Tags] + parameters: + - name: content_type + in: path + required: true + description: | + The content type identifier (e.g. `com_content`, `menu`, + `com_mokoshop`). Must match the pattern `[a-z][a-z0-9_.]*`. + schema: + type: string + pattern: "^[a-z][a-z0-9_.]*$" + example: com_content + - name: content_id + in: path + required: true + description: The content item ID + schema: + type: integer + minimum: 1 + example: 42 + responses: + "200": + description: The matching OG tag resource + content: + application/vnd.api+json: + schema: + $ref: "#/components/schemas/TagDocument" + "400": + description: Missing or invalid content_type / content_id + content: + application/vnd.api+json: + schema: + $ref: "#/components/schemas/ErrorResponse" + example: + errors: + - title: Bad Request + status: 400 + detail: "content_type and content_id are required" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + description: No OG tag found for the given content_type and content_id + content: + application/vnd.api+json: + schema: + $ref: "#/components/schemas/ErrorResponse" + example: + errors: + - title: Not Found + status: 404 + detail: "OG tag not found for com_content:42" + +components: + securitySchemes: + apiToken: + type: apiKey + name: X-Joomla-Token + in: header + description: | + Joomla API token. Can also be passed as the `api-token` query + parameter. Generate a token from the Joomla administrator panel + under Users > Manage > [user] > Joomla API Token tab. + + parameters: + TagId: + name: id + in: path + required: true + description: The OG tag record ID + schema: + type: integer + minimum: 1 + example: 1 + + schemas: + TagAttributes: + type: object + description: Full set of OG tag attributes returned by the API + properties: + content_type: + type: string + description: | + Content type identifier (e.g. `com_content`, `menu`, + `com_mokoshop`). Must match `[a-z][a-z0-9_.]*`. + pattern: "^[a-z][a-z0-9_.]*$" + maxLength: 100 + example: com_content + content_id: + type: integer + description: The ID of the associated content item + minimum: 1 + example: 42 + og_title: + type: string + description: Open Graph title (`og:title`) + maxLength: 255 + example: "My Article Title" + og_description: + type: string + description: Open Graph description (`og:description`) + example: "A brief description for social sharing." + og_image: + type: string + description: Relative path to the Open Graph image (`og:image`) + maxLength: 512 + example: "images/mokoog/og-banner.jpg" + og_type: + type: string + description: Open Graph type (`og:type`) + default: article + enum: + - article + - website + - product + - profile + - book + - music.song + - music.album + - video.movie + - video.episode + - video.other + example: article + seo_title: + type: string + description: SEO page title (used in `` tag) + maxLength: 70 + example: "My Article | Example Site" + meta_description: + type: string + description: Meta description for search engines + maxLength: 200 + example: "A brief meta description for search engines." + robots: + type: string + description: | + Comma-separated robots directives. Valid directives: `index`, + `noindex`, `follow`, `nofollow`, `none`, `noarchive`, + `nosnippet`, `noimageindex`, `max-snippet`, `max-image-preview`. + maxLength: 100 + example: "index, follow" + canonical_url: + type: string + format: uri + description: Canonical URL for the page + maxLength: 512 + example: "https://example.com/my-article" + language: + type: string + description: Joomla language tag (`*` for all languages) + maxLength: 7 + default: "*" + example: "*" + published: + type: integer + description: Published state (1 = published, 0 = unpublished) + enum: [0, 1] + default: 1 + example: 1 + created: + type: string + format: date-time + description: Record creation timestamp (read-only) + readOnly: true + example: "2026-06-01T12:00:00+00:00" + modified: + type: string + format: date-time + description: Last modification timestamp (read-only) + readOnly: true + example: "2026-06-15T08:30:00+00:00" + + TagResource: + type: object + description: A single OG tag in JSON:API resource format + required: [type, id, attributes] + properties: + type: + type: string + enum: [tags] + example: tags + id: + type: string + description: The record ID as a string (per JSON:API spec) + example: "1" + attributes: + $ref: "#/components/schemas/TagAttributes" + + TagDocument: + type: object + description: JSON:API document containing a single tag resource + properties: + links: + type: object + properties: + self: + type: string + example: "/api/index.php/v1/mokoog/tags/1" + data: + $ref: "#/components/schemas/TagResource" + + TagCollection: + type: object + description: JSON:API document containing a collection of tag resources + properties: + links: + type: object + properties: + self: + type: string + example: "/api/index.php/v1/mokoog/tags" + data: + type: array + items: + $ref: "#/components/schemas/TagResource" + meta: + type: object + properties: + total-pages: + type: integer + description: Total number of pages available + example: 1 + + TagCreateRequest: + type: object + description: Request body for creating a new OG tag + required: + - content_type + - content_id + properties: + content_type: + type: string + pattern: "^[a-z][a-z0-9_.]*$" + maxLength: 100 + example: com_content + content_id: + type: integer + minimum: 1 + example: 42 + og_title: + type: string + maxLength: 255 + og_description: + type: string + og_image: + type: string + maxLength: 512 + og_type: + type: string + default: article + enum: + - article + - website + - product + - profile + - book + - music.song + - music.album + - video.movie + - video.episode + - video.other + og_video: + type: string + format: uri + description: Open Graph video URL (`og:video`) + maxLength: 512 + seo_title: + type: string + maxLength: 70 + meta_description: + type: string + maxLength: 200 + robots: + type: string + maxLength: 100 + canonical_url: + type: string + format: uri + maxLength: 512 + language: + type: string + maxLength: 7 + default: "*" + published: + type: integer + enum: [0, 1] + default: 1 + + TagUpdateRequest: + type: object + description: | + Request body for updating an OG tag. All fields are optional; only + supplied fields are modified. + properties: + og_title: + type: string + maxLength: 255 + og_description: + type: string + og_image: + type: string + maxLength: 512 + og_type: + type: string + enum: + - article + - website + - product + - profile + - book + - music.song + - music.album + - video.movie + - video.episode + - video.other + og_video: + type: string + format: uri + maxLength: 512 + seo_title: + type: string + maxLength: 70 + meta_description: + type: string + maxLength: 200 + robots: + type: string + maxLength: 100 + canonical_url: + type: string + format: uri + maxLength: 512 + language: + type: string + maxLength: 7 + published: + type: integer + enum: [0, 1] + + ErrorResponse: + type: object + description: JSON:API error response + properties: + errors: + type: array + items: + type: object + properties: + title: + type: string + example: Not Found + status: + type: integer + example: 404 + detail: + type: string + example: "Item not found." + + responses: + BadRequest: + description: Invalid request data + content: + application/vnd.api+json: + schema: + $ref: "#/components/schemas/ErrorResponse" + example: + errors: + - title: Bad Request + status: 400 + detail: "Content type is required." + + Unauthorized: + description: Missing or invalid API token + content: + application/vnd.api+json: + schema: + $ref: "#/components/schemas/ErrorResponse" + example: + errors: + - title: Forbidden + status: 403 + detail: "You are not authorised to access this resource." + + NotFound: + description: Resource not found + content: + application/vnd.api+json: + schema: + $ref: "#/components/schemas/ErrorResponse" + example: + errors: + - title: Not Found + status: 404 + detail: "Item not found." From cbebaecc225dec1e629ab423ff387851c0ea9b42 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <jmiller-moko@noreply.git.mokoconsulting.tech> Date: Tue, 23 Jun 2026 12:31:18 -0500 Subject: [PATCH 15/41] test: add PHPUnit test suite with JsonLdBuilder unit tests 16 unit tests covering FAQ, HowTo, Event, Recipe, LocalBusiness, and VideoObject schema builders plus toScriptTag XSS escaping. Closes #75 --- .gitignore | 1 + composer.json | 18 +- phpunit.xml.dist | 17 ++ tests/Unit/Helper/JsonLdBuilderTest.php | 257 ++++++++++++++++++++++++ tests/bootstrap.php | 13 ++ 5 files changed, 304 insertions(+), 2 deletions(-) create mode 100644 phpunit.xml.dist create mode 100644 tests/Unit/Helper/JsonLdBuilderTest.php create mode 100644 tests/bootstrap.php diff --git a/.gitignore b/.gitignore index 8affbb6..d027d61 100644 --- a/.gitignore +++ b/.gitignore @@ -156,6 +156,7 @@ vendor/ composer.lock *.phar codeception.phar +.phpunit.cache/ .phpunit.result.cache .php_cs.cache .php-cs-fixer.cache diff --git a/composer.json b/composer.json index c21717f..a5fd414 100644 --- a/composer.json +++ b/composer.json @@ -15,9 +15,23 @@ "php": ">=8.1" }, "require-dev": { - "squizlabs/php_codesniffer": "^3.7", + "joomla/coding-standards": "^3.0", "phpstan/phpstan": "^1.10", - "joomla/coding-standards": "^3.0" + "phpunit/phpunit": "^10.5", + "squizlabs/php_codesniffer": "^3.7" + }, + "autoload": { + "psr-4": { + "Joomla\\Plugin\\System\\MokoOG\\": "source/packages/plg_system_mokoog/src/", + "Joomla\\Plugin\\Content\\MokoOG\\": "source/packages/plg_content_mokoog/src/", + "Joomla\\Plugin\\WebServices\\MokoOG\\": "source/packages/plg_webservices_mokoog/src/", + "Joomla\\Component\\MokoOG\\Administrator\\": "source/packages/com_mokoog/src/" + } + }, + "autoload-dev": { + "psr-4": { + "Mokoconsulting\\MokoOG\\Tests\\": "tests/" + } }, "minimum-stability": "alpha", "prefer-stable": true, diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..9f4fb2a --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd" + bootstrap="tests/bootstrap.php" + colors="true" + cacheDirectory=".phpunit.cache"> + <testsuites> + <testsuite name="Unit"> + <directory>tests/Unit</directory> + </testsuite> + </testsuites> + <source> + <include> + <directory>source/packages</directory> + </include> + </source> +</phpunit> diff --git a/tests/Unit/Helper/JsonLdBuilderTest.php b/tests/Unit/Helper/JsonLdBuilderTest.php new file mode 100644 index 0000000..e7b3921 --- /dev/null +++ b/tests/Unit/Helper/JsonLdBuilderTest.php @@ -0,0 +1,257 @@ +<?php + +/** + * @package MokoSuiteOpenGraph + * @subpackage Tests + * @author Moko Consulting <hello@mokoconsulting.tech> + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +namespace Mokoconsulting\MokoOG\Tests\Unit\Helper; + +use Joomla\Plugin\System\MokoOG\Helper\JsonLdBuilder; +use PHPUnit\Framework\TestCase; + +class JsonLdBuilderTest extends TestCase +{ + // ── FAQPage ────────────────────────────────────────────────────── + + public function testBuildFaqReturnsNullForEmptyArray(): void + { + $this->assertNull(JsonLdBuilder::buildFaq([])); + } + + public function testBuildFaqSkipsEmptyQuestions(): void + { + $faqs = [ + ['question' => '', 'answer' => 'An answer'], + ['question' => 'Valid?', 'answer' => ''], + ['question' => ' ', 'answer' => 'Still empty'], + ]; + + $this->assertNull(JsonLdBuilder::buildFaq($faqs)); + } + + public function testBuildFaqReturnsValidSchema(): void + { + $faqs = [ + ['question' => 'What is OG?', 'answer' => 'Open Graph protocol.'], + ['question' => 'Why use it?', 'answer' => 'Better social previews.'], + ]; + + $result = JsonLdBuilder::buildFaq($faqs); + + $this->assertNotNull($result); + $this->assertSame('https://schema.org', $result['@context']); + $this->assertSame('FAQPage', $result['@type']); + $this->assertCount(2, $result['mainEntity']); + $this->assertSame('Question', $result['mainEntity'][0]['@type']); + $this->assertSame('What is OG?', $result['mainEntity'][0]['name']); + $this->assertSame('Open Graph protocol.', $result['mainEntity'][0]['acceptedAnswer']['text']); + } + + // ── HowTo ──────────────────────────────────────────────────────── + + public function testBuildHowToReturnsNullForEmptySteps(): void + { + $this->assertNull(JsonLdBuilder::buildHowTo('Test', [])); + $this->assertNull(JsonLdBuilder::buildHowTo('Test', ['', ' '])); + } + + public function testBuildHowToReturnsValidSchema(): void + { + $result = JsonLdBuilder::buildHowTo('Install Joomla', ['Download ZIP', 'Upload files', 'Run installer']); + + $this->assertNotNull($result); + $this->assertSame('HowTo', $result['@type']); + $this->assertSame('Install Joomla', $result['name']); + $this->assertCount(3, $result['step']); + $this->assertSame(1, $result['step'][0]['position']); + $this->assertSame('HowToStep', $result['step'][0]['@type']); + $this->assertSame('Download ZIP', $result['step'][0]['text']); + $this->assertArrayNotHasKey('image', $result); + } + + public function testBuildHowToIncludesImageWhenProvided(): void + { + $result = JsonLdBuilder::buildHowTo('Fix a bike', ['Remove wheel'], 'https://example.com/bike.jpg'); + + $this->assertNotNull($result); + $this->assertSame('https://example.com/bike.jpg', $result['image']); + } + + // ── Recipe ─────────────────────────────────────────────────────── + + public function testBuildRecipeReturnsNullWhenNoData(): void + { + $data = (object) ['name' => '', 'description' => '']; + + $this->assertNull(JsonLdBuilder::buildRecipe($data)); + } + + public function testBuildRecipeCalculatesTotalTime(): void + { + $data = (object) [ + 'name' => 'Pasta', + 'prepTime' => 'PT15M', + 'cookTime' => 'PT30M', + ]; + + $result = JsonLdBuilder::buildRecipe($data); + + $this->assertNotNull($result); + $this->assertSame('Recipe', $result['@type']); + $this->assertSame('PT45M', $result['totalTime']); + } + + public function testBuildRecipeSplitsIngredientsByNewline(): void + { + $data = (object) [ + 'name' => 'Salad', + 'ingredients' => "Lettuce\nTomato\nOnion", + ]; + + $result = JsonLdBuilder::buildRecipe($data); + + $this->assertNotNull($result); + $this->assertSame(['Lettuce', 'Tomato', 'Onion'], $result['recipeIngredient']); + } + + // ── Event ──────────────────────────────────────────────────────── + + public function testBuildEventReturnsNullWithoutStartDate(): void + { + $data = (object) ['name' => 'Conference', 'startDate' => '']; + + $this->assertNull(JsonLdBuilder::buildEvent($data)); + } + + public function testBuildEventIncludesLocationAndOffers(): void + { + $data = (object) [ + 'name' => 'Tech Summit', + 'startDate' => '2026-09-01T09:00:00', + 'endDate' => '2026-09-01T17:00:00', + 'location' => (object) [ + 'name' => 'Convention Center', + 'address' => '123 Main St', + ], + 'offers' => (object) [ + 'price' => '99.00', + 'currency' => 'EUR', + 'url' => 'https://example.com/tickets', + ], + ]; + + $result = JsonLdBuilder::buildEvent($data); + + $this->assertNotNull($result); + $this->assertSame('Event', $result['@type']); + $this->assertSame('2026-09-01T09:00:00', $result['startDate']); + $this->assertSame('2026-09-01T17:00:00', $result['endDate']); + $this->assertSame('Place', $result['location']['@type']); + $this->assertSame('Convention Center', $result['location']['name']); + $this->assertSame('Offer', $result['offers']['@type']); + $this->assertSame('99.00', $result['offers']['price']); + $this->assertSame('EUR', $result['offers']['priceCurrency']); + } + + // ── LocalBusiness ──────────────────────────────────────────────── + + public function testBuildLocalBusinessReturnsNullWithoutName(): void + { + $params = $this->createParamsMock([]); + + $this->assertNull(JsonLdBuilder::buildLocalBusiness($params)); + } + + public function testBuildLocalBusinessIncludesAddress(): void + { + $params = $this->createParamsMock([ + 'business_name' => 'Moko Consulting', + 'street_address' => '456 Oak Ave', + 'city' => 'Austin', + 'region' => 'TX', + 'postal_code' => '78701', + 'country' => 'US', + 'telephone' => '+1-555-0100', + ]); + + $result = JsonLdBuilder::buildLocalBusiness($params); + + $this->assertNotNull($result); + $this->assertSame('LocalBusiness', $result['@type']); + $this->assertSame('Moko Consulting', $result['name']); + $this->assertSame('PostalAddress', $result['address']['@type']); + $this->assertSame('456 Oak Ave', $result['address']['streetAddress']); + $this->assertSame('Austin', $result['address']['addressLocality']); + $this->assertSame('TX', $result['address']['addressRegion']); + $this->assertSame('78701', $result['address']['postalCode']); + $this->assertSame('US', $result['address']['addressCountry']); + $this->assertSame('+1-555-0100', $result['telephone']); + } + + // ── VideoObject ────────────────────────────────────────────────── + + public function testBuildVideoReturnsNullForEmptyUrl(): void + { + $this->assertNull(JsonLdBuilder::buildVideo('')); + } + + public function testBuildVideoAddsEmbedUrlForYoutube(): void + { + $result = JsonLdBuilder::buildVideo( + 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', + 'Test Video', + 'A description' + ); + + $this->assertNotNull($result); + $this->assertSame('VideoObject', $result['@type']); + $this->assertSame('https://www.youtube.com/watch?v=dQw4w9WgXcQ', $result['contentUrl']); + $this->assertSame('https://www.youtube.com/embed/dQw4w9WgXcQ', $result['embedUrl']); + $this->assertSame('Test Video', $result['name']); + } + + // ── toScriptTag ────────────────────────────────────────────────── + + public function testToScriptTagEscapesClosingScriptTags(): void + { + $schema = [ + '@context' => 'https://schema.org', + '@type' => 'Article', + 'headline' => 'Test </script><script>alert(1)</script>', + ]; + + $output = JsonLdBuilder::toScriptTag($schema); + + $this->assertStringStartsWith('<script type="application/ld+json">', $output); + $this->assertStringEndsWith('</script>', $output); + // The closing </script> inside the JSON must be escaped + $this->assertStringNotContainsString('</script><script>', $output); + $this->assertStringContainsString('<\\/script>', $output); + } + + // ── Helper ─────────────────────────────────────────────────────── + + /** + * Create a mock object that mimics Joomla's Registry->get($key, $default). + */ + private function createParamsMock(array $values): object + { + return new class ($values) { + private array $data; + + public function __construct(array $data) + { + $this->data = $data; + } + + public function get(string $key, mixed $default = ''): mixed + { + return $this->data[$key] ?? $default; + } + }; + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..36f2c94 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,13 @@ +<?php + +/** + * @package MokoSuiteOpenGraph + * @subpackage Tests + * @author Moko Consulting <hello@mokoconsulting.tech> + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +define('_JEXEC', 1); + +require_once __DIR__ . '/../vendor/autoload.php'; From 49d644566af438a55b192c1263ea60d74ff02145 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <jmiller-moko@noreply.git.mokoconsulting.tech> Date: Tue, 23 Jun 2026 12:53:47 -0500 Subject: [PATCH 16/41] feat: add custom schema, AI generation, XML sitemap, platform images - Custom JSON-LD schema builder: per-article textarea for arbitrary structured data with JSON validation. Closes #70 - AI-powered meta generation: Generate with AI buttons for OG title and description, supports Claude and OpenAI APIs. Closes #71 - XML sitemap: auto-generates sitemap.xml on article save, respects noindex directives. Closes #72 - Per-platform image resizing: Twitter 1200x600, Pinterest 1000x1500, WhatsApp 400x400 alongside default Facebook 1200x630. Closes #74 - DB migration 01.05.00: adds custom_schema column --- .../packages/com_mokoog/sql/install.mysql.sql | 1 + .../com_mokoog/sql/updates/mysql/01.05.00.sql | 1 + .../plg_content_mokoog/forms/mokoog.xml | 4 + .../language/en-GB/plg_content_mokoog.ini | 5 + .../language/en-US/plg_content_mokoog.ini | 7 +- .../plg_content_mokoog/media/js/preview.js | 43 +++++ .../src/Extension/MokoOGContent.php | 21 ++- .../language/en-GB/plg_system_mokoog.ini | 18 +++ .../language/en-US/plg_system_mokoog.ini | 18 +++ source/packages/plg_system_mokoog/mokoog.xml | 34 ++++ .../src/Extension/MokoOG.php | 147 +++++++++++++++++- .../src/Helper/ImageHelper.php | 131 ++++++++++++++++ .../src/Helper/SitemapBuilder.php | 107 +++++++++++++ 13 files changed, 534 insertions(+), 3 deletions(-) create mode 100644 source/packages/com_mokoog/sql/updates/mysql/01.05.00.sql create mode 100644 source/packages/plg_system_mokoog/src/Helper/SitemapBuilder.php diff --git a/source/packages/com_mokoog/sql/install.mysql.sql b/source/packages/com_mokoog/sql/install.mysql.sql index 86e2c45..ac3112c 100644 --- a/source/packages/com_mokoog/sql/install.mysql.sql +++ b/source/packages/com_mokoog/sql/install.mysql.sql @@ -15,6 +15,7 @@ CREATE TABLE IF NOT EXISTS `#__mokoog_tags` ( `og_video` VARCHAR(512) NOT NULL DEFAULT '', `event_data` TEXT NULL, `recipe_data` TEXT NULL, + `custom_schema` TEXT NULL, `seo_title` VARCHAR(70) NOT NULL DEFAULT '', `meta_description` VARCHAR(200) NOT NULL DEFAULT '', `robots` VARCHAR(100) NOT NULL DEFAULT '', diff --git a/source/packages/com_mokoog/sql/updates/mysql/01.05.00.sql b/source/packages/com_mokoog/sql/updates/mysql/01.05.00.sql new file mode 100644 index 0000000..283e14e --- /dev/null +++ b/source/packages/com_mokoog/sql/updates/mysql/01.05.00.sql @@ -0,0 +1 @@ +ALTER TABLE `#__mokoog_tags` ADD COLUMN `custom_schema` TEXT NULL AFTER `canonical_url`; diff --git a/source/packages/plg_content_mokoog/forms/mokoog.xml b/source/packages/plg_content_mokoog/forms/mokoog.xml index 08635ce..6dd2811 100644 --- a/source/packages/plg_content_mokoog/forms/mokoog.xml +++ b/source/packages/plg_content_mokoog/forms/mokoog.xml @@ -121,5 +121,9 @@ <field name="recipe_category" type="text" label="PLG_CONTENT_MOKOOG_FIELD_RECIPE_CATEGORY" description="PLG_CONTENT_MOKOOG_FIELD_RECIPE_CATEGORY_DESC" filter="string" hint="Dessert" /> <field name="recipe_cuisine" type="text" label="PLG_CONTENT_MOKOOG_FIELD_RECIPE_CUISINE" description="PLG_CONTENT_MOKOOG_FIELD_RECIPE_CUISINE_DESC" filter="string" hint="Italian" /> </fieldset> + <fieldset name="mokoog_custom_schema" label="PLG_CONTENT_MOKOOG_FIELDSET_CUSTOM_SCHEMA_LABEL" + description="PLG_CONTENT_MOKOOG_FIELDSET_CUSTOM_SCHEMA_DESC"> + <field name="custom_schema" type="textarea" label="PLG_CONTENT_MOKOOG_FIELD_CUSTOM_SCHEMA" description="PLG_CONTENT_MOKOOG_FIELD_CUSTOM_SCHEMA_DESC" filter="raw" rows="12" class="input-xxlarge" /> + </fieldset> </fields> </form> diff --git a/source/packages/plg_content_mokoog/language/en-GB/plg_content_mokoog.ini b/source/packages/plg_content_mokoog/language/en-GB/plg_content_mokoog.ini index 85fe8f2..1ade0e2 100644 --- a/source/packages/plg_content_mokoog/language/en-GB/plg_content_mokoog.ini +++ b/source/packages/plg_content_mokoog/language/en-GB/plg_content_mokoog.ini @@ -63,3 +63,8 @@ PLG_CONTENT_MOKOOG_FIELD_RECIPE_CATEGORY="Recipe Category" PLG_CONTENT_MOKOOG_FIELD_RECIPE_CATEGORY_DESC="Category (e.g. Dessert, Appetizer, Main course)." PLG_CONTENT_MOKOOG_FIELD_RECIPE_CUISINE="Cuisine" PLG_CONTENT_MOKOOG_FIELD_RECIPE_CUISINE_DESC="Type of cuisine (e.g. Italian, Mexican, American)." + +PLG_CONTENT_MOKOOG_FIELDSET_CUSTOM_SCHEMA_LABEL="Custom Schema" +PLG_CONTENT_MOKOOG_FIELDSET_CUSTOM_SCHEMA_DESC="Add custom JSON-LD structured data for this page." +PLG_CONTENT_MOKOOG_FIELD_CUSTOM_SCHEMA="Custom JSON-LD" +PLG_CONTENT_MOKOOG_FIELD_CUSTOM_SCHEMA_DESC="Enter valid JSON-LD structured data. The @context will be added automatically if missing. Use for schema types not covered by built-in options (e.g. Course, JobPosting, SoftwareApplication)." diff --git a/source/packages/plg_content_mokoog/language/en-US/plg_content_mokoog.ini b/source/packages/plg_content_mokoog/language/en-US/plg_content_mokoog.ini index 9a7634a..1ade0e2 100644 --- a/source/packages/plg_content_mokoog/language/en-US/plg_content_mokoog.ini +++ b/source/packages/plg_content_mokoog/language/en-US/plg_content_mokoog.ini @@ -62,4 +62,9 @@ PLG_CONTENT_MOKOOG_FIELD_RECIPE_INGREDIENTS_DESC="One ingredient per line." PLG_CONTENT_MOKOOG_FIELD_RECIPE_CATEGORY="Recipe Category" PLG_CONTENT_MOKOOG_FIELD_RECIPE_CATEGORY_DESC="Category (e.g. Dessert, Appetizer, Main course)." PLG_CONTENT_MOKOOG_FIELD_RECIPE_CUISINE="Cuisine" -PLG_CONTENT_MKOOG_FIELD_RECIPE_CUISINE_DESC="Type of cuisine (e.g. Italian, Mexican, American)." +PLG_CONTENT_MOKOOG_FIELD_RECIPE_CUISINE_DESC="Type of cuisine (e.g. Italian, Mexican, American)." + +PLG_CONTENT_MOKOOG_FIELDSET_CUSTOM_SCHEMA_LABEL="Custom Schema" +PLG_CONTENT_MOKOOG_FIELDSET_CUSTOM_SCHEMA_DESC="Add custom JSON-LD structured data for this page." +PLG_CONTENT_MOKOOG_FIELD_CUSTOM_SCHEMA="Custom JSON-LD" +PLG_CONTENT_MOKOOG_FIELD_CUSTOM_SCHEMA_DESC="Enter valid JSON-LD structured data. The @context will be added automatically if missing. Use for schema types not covered by built-in options (e.g. Course, JobPosting, SoftwareApplication)." diff --git a/source/packages/plg_content_mokoog/media/js/preview.js b/source/packages/plg_content_mokoog/media/js/preview.js index 77192b2..9cbc401 100644 --- a/source/packages/plg_content_mokoog/media/js/preview.js +++ b/source/packages/plg_content_mokoog/media/js/preview.js @@ -53,6 +53,49 @@ document.addEventListener('DOMContentLoaded', function () { refresh(); }); + // AI Generate buttons + ['ogTitle', 'ogDesc'].forEach(function(fieldKey) { + var field = fields[fieldKey]; + if (!field) return; + + var btn = document.createElement('button'); + btn.type = 'button'; + btn.className = 'btn btn-sm btn-outline-primary mokoog-ai-btn'; + btn.textContent = 'Generate with AI'; + btn.dataset.target = fieldKey; + field.parentNode.appendChild(btn); + + btn.addEventListener('click', function() { + var articleTitle = fields.articleTitle ? fields.articleTitle.value : ''; + btn.disabled = true; + btn.textContent = 'Generating...'; + + var formData = new FormData(); + formData.append('task', 'mokoog.aiGenerate'); + formData.append('field', fieldKey === 'ogTitle' ? 'title' : 'description'); + formData.append('article_title', articleTitle); + formData.append(Joomla.getOptions('csrf.token'), 1); + + fetch(window.location.origin + '/administrator/index.php?option=com_ajax&plugin=mokoog&group=system&format=json', { + method: 'POST', + body: formData + }) + .then(function(r) { return r.json(); }) + .then(function(data) { + if (data.data && data.data[0]) { + field.value = data.data[0]; + field.dispatchEvent(new Event('input')); + } + btn.disabled = false; + btn.textContent = 'Generate with AI'; + }) + .catch(function() { + btn.disabled = false; + btn.textContent = 'Generate with AI'; + }); + }); + }); + // Find the mokoog fieldset and insert preview after it var fieldset = document.querySelector('[data-showon-id="mokoog"]') || document.getElementById('attrib-mokoog') || diff --git a/source/packages/plg_content_mokoog/src/Extension/MokoOGContent.php b/source/packages/plg_content_mokoog/src/Extension/MokoOGContent.php index 9d93f0e..dd7d709 100644 --- a/source/packages/plg_content_mokoog/src/Extension/MokoOGContent.php +++ b/source/packages/plg_content_mokoog/src/Extension/MokoOGContent.php @@ -212,7 +212,7 @@ final class MokoOGContent extends CMSPlugin implements SubscriberInterface $query = $db->getQuery(true) ->select($db->quoteName([ 'og_title', 'og_description', 'og_image', 'og_type', 'og_video', - 'event_data', 'recipe_data', + 'event_data', 'recipe_data', 'custom_schema', 'seo_title', 'meta_description', 'robots', 'canonical_url', ])) ->from($db->quoteName('#__mokoog_tags')) @@ -270,6 +270,7 @@ final class MokoOGContent extends CMSPlugin implements SubscriberInterface 'og_video' => $this->sanitizeUrl($ogData['og_video'] ?? ''), 'event_data' => $this->packJsonFields($ogData, ['event_start', 'event_end', 'event_location', 'event_address', 'event_price', 'event_currency', 'event_url']), 'recipe_data' => $this->packJsonFields($ogData, ['recipe_prep_time', 'recipe_cook_time', 'recipe_yield', 'recipe_calories', 'recipe_ingredients', 'recipe_category', 'recipe_cuisine']), + 'custom_schema' => $this->validateJson($ogData['custom_schema'] ?? ''), 'seo_title' => strip_tags(trim($ogData['seo_title'] ?? '')), 'meta_description' => strip_tags(trim($ogData['meta_description'] ?? '')), 'robots' => trim($robots), @@ -310,6 +311,24 @@ final class MokoOGContent extends CMSPlugin implements SubscriberInterface return !empty($data) ? json_encode($data) : ''; } + /** + * Validate a JSON string — returns trimmed JSON or empty string if invalid. + * + * @param string $json Raw JSON input + * + * @return string + */ + private function validateJson(string $json): string + { + $json = trim($json); + + if ($json === '' || json_decode($json) === null) { + return ''; + } + + return $json; + } + /** * Sanitize a URL to only allow http/https schemes. * 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 b6df7cb..3791562 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 @@ -75,3 +75,21 @@ PLG_SYSTEM_MOKOOG_FIELD_LB_LONGITUDE="Longitude" PLG_SYSTEM_MOKOOG_FIELD_LB_LONGITUDE_DESC="Geographic longitude of your business." PLG_SYSTEM_MOKOOG_FIELD_LB_PRICE_RANGE="Price Range" PLG_SYSTEM_MOKOOG_FIELD_LB_PRICE_RANGE_DESC="Price range indicator (e.g. $, $$, $$$)." +PLG_SYSTEM_MOKOOG_FIELD_PLATFORM_RESIZE="Per-platform Image Sizes" +PLG_SYSTEM_MOKOOG_FIELD_PLATFORM_RESIZE_DESC="Generate platform-specific image sizes (Twitter 1200x600, Pinterest 1000x1500, WhatsApp 400x400) in addition to the default Facebook 1200x630." + +PLG_SYSTEM_MOKOOG_FIELDSET_SITEMAP="XML Sitemap" +PLG_SYSTEM_MOKOOG_FIELD_SITEMAP_ENABLED="Enable Sitemap" +PLG_SYSTEM_MOKOOG_FIELD_SITEMAP_ENABLED_DESC="Auto-generate sitemap.xml when articles are saved. Respects noindex robots directives." +PLG_SYSTEM_MOKOOG_FIELD_SITEMAP_CHANGEFREQ="Change Frequency" +PLG_SYSTEM_MOKOOG_FIELD_SITEMAP_CHANGEFREQ_DESC="Default change frequency for sitemap entries." + +PLG_SYSTEM_MOKOOG_FIELDSET_AI="AI Meta Generation" +PLG_SYSTEM_MOKOOG_FIELD_AI_ENABLED="Enable AI Generation" +PLG_SYSTEM_MOKOOG_FIELD_AI_ENABLED_DESC="Show Generate with AI buttons next to OG title and description fields." +PLG_SYSTEM_MOKOOG_FIELD_AI_PROVIDER="AI Provider" +PLG_SYSTEM_MOKOOG_FIELD_AI_PROVIDER_DESC="Select the AI API provider." +PLG_SYSTEM_MOKOOG_FIELD_AI_API_KEY="API Key" +PLG_SYSTEM_MOKOOG_FIELD_AI_API_KEY_DESC="Your AI provider API key." +PLG_SYSTEM_MOKOOG_FIELD_AI_MODEL="Model" +PLG_SYSTEM_MOKOOG_FIELD_AI_MODEL_DESC="AI model to use for generation." 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 b6df7cb..3791562 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 @@ -75,3 +75,21 @@ PLG_SYSTEM_MOKOOG_FIELD_LB_LONGITUDE="Longitude" PLG_SYSTEM_MOKOOG_FIELD_LB_LONGITUDE_DESC="Geographic longitude of your business." PLG_SYSTEM_MOKOOG_FIELD_LB_PRICE_RANGE="Price Range" PLG_SYSTEM_MOKOOG_FIELD_LB_PRICE_RANGE_DESC="Price range indicator (e.g. $, $$, $$$)." +PLG_SYSTEM_MOKOOG_FIELD_PLATFORM_RESIZE="Per-platform Image Sizes" +PLG_SYSTEM_MOKOOG_FIELD_PLATFORM_RESIZE_DESC="Generate platform-specific image sizes (Twitter 1200x600, Pinterest 1000x1500, WhatsApp 400x400) in addition to the default Facebook 1200x630." + +PLG_SYSTEM_MOKOOG_FIELDSET_SITEMAP="XML Sitemap" +PLG_SYSTEM_MOKOOG_FIELD_SITEMAP_ENABLED="Enable Sitemap" +PLG_SYSTEM_MOKOOG_FIELD_SITEMAP_ENABLED_DESC="Auto-generate sitemap.xml when articles are saved. Respects noindex robots directives." +PLG_SYSTEM_MOKOOG_FIELD_SITEMAP_CHANGEFREQ="Change Frequency" +PLG_SYSTEM_MOKOOG_FIELD_SITEMAP_CHANGEFREQ_DESC="Default change frequency for sitemap entries." + +PLG_SYSTEM_MOKOOG_FIELDSET_AI="AI Meta Generation" +PLG_SYSTEM_MOKOOG_FIELD_AI_ENABLED="Enable AI Generation" +PLG_SYSTEM_MOKOOG_FIELD_AI_ENABLED_DESC="Show Generate with AI buttons next to OG title and description fields." +PLG_SYSTEM_MOKOOG_FIELD_AI_PROVIDER="AI Provider" +PLG_SYSTEM_MOKOOG_FIELD_AI_PROVIDER_DESC="Select the AI API provider." +PLG_SYSTEM_MOKOOG_FIELD_AI_API_KEY="API Key" +PLG_SYSTEM_MOKOOG_FIELD_AI_API_KEY_DESC="Your AI provider API key." +PLG_SYSTEM_MOKOOG_FIELD_AI_MODEL="Model" +PLG_SYSTEM_MOKOOG_FIELD_AI_MODEL_DESC="AI model to use for generation." diff --git a/source/packages/plg_system_mokoog/mokoog.xml b/source/packages/plg_system_mokoog/mokoog.xml index 54d956f..aa97e32 100644 --- a/source/packages/plg_system_mokoog/mokoog.xml +++ b/source/packages/plg_system_mokoog/mokoog.xml @@ -158,6 +158,17 @@ <option value="1">JYES</option> <option value="0">JNO</option> </field> + <field + name="platform_resize" + type="radio" + label="PLG_SYSTEM_MOKOOG_FIELD_PLATFORM_RESIZE" + description="PLG_SYSTEM_MOKOOG_FIELD_PLATFORM_RESIZE_DESC" + default="0" + class="btn-group" + > + <option value="1">JYES</option> + <option value="0">JNO</option> + </field> <field name="jsonld_enabled" type="radio" @@ -332,6 +343,29 @@ filter="string" /> </fieldset> + <fieldset name="sitemap" label="PLG_SYSTEM_MOKOOG_FIELDSET_SITEMAP"> + <field name="sitemap_enabled" type="radio" label="PLG_SYSTEM_MOKOOG_FIELD_SITEMAP_ENABLED" description="PLG_SYSTEM_MOKOOG_FIELD_SITEMAP_ENABLED_DESC" default="0" class="btn-group"> + <option value="1">JYES</option> + <option value="0">JNO</option> + </field> + <field name="sitemap_changefreq" type="list" label="PLG_SYSTEM_MOKOOG_FIELD_SITEMAP_CHANGEFREQ" description="PLG_SYSTEM_MOKOOG_FIELD_SITEMAP_CHANGEFREQ_DESC" default="weekly"> + <option value="daily">Daily</option> + <option value="weekly">Weekly</option> + <option value="monthly">Monthly</option> + </field> + </fieldset> + <fieldset name="ai" label="PLG_SYSTEM_MOKOOG_FIELDSET_AI"> + <field name="ai_enabled" type="radio" label="PLG_SYSTEM_MOKOOG_FIELD_AI_ENABLED" description="PLG_SYSTEM_MOKOOG_FIELD_AI_ENABLED_DESC" default="0" class="btn-group"> + <option value="1">JYES</option> + <option value="0">JNO</option> + </field> + <field name="ai_provider" type="list" label="PLG_SYSTEM_MOKOOG_FIELD_AI_PROVIDER" description="PLG_SYSTEM_MOKOOG_FIELD_AI_PROVIDER_DESC" default="claude"> + <option value="claude">Claude (Anthropic)</option> + <option value="openai">OpenAI</option> + </field> + <field name="ai_api_key" type="password" label="PLG_SYSTEM_MOKOOG_FIELD_AI_API_KEY" description="PLG_SYSTEM_MOKOOG_FIELD_AI_API_KEY_DESC" filter="string" /> + <field name="ai_model" type="text" label="PLG_SYSTEM_MOKOOG_FIELD_AI_MODEL" description="PLG_SYSTEM_MOKOOG_FIELD_AI_MODEL_DESC" default="claude-haiku-4-5-20251001" filter="string" /> + </fieldset> </fields> </config> </extension> diff --git a/source/packages/plg_system_mokoog/src/Extension/MokoOG.php b/source/packages/plg_system_mokoog/src/Extension/MokoOG.php index 099a6c9..bb59693 100644 --- a/source/packages/plg_system_mokoog/src/Extension/MokoOG.php +++ b/source/packages/plg_system_mokoog/src/Extension/MokoOG.php @@ -19,6 +19,7 @@ use Joomla\Event\Event; use Joomla\Event\SubscriberInterface; use Joomla\Plugin\System\MokoOG\Helper\ImageHelper; use Joomla\Plugin\System\MokoOG\Helper\JsonLdBuilder; +use Joomla\Plugin\System\MokoOG\Helper\SitemapBuilder; final class MokoOG extends CMSPlugin implements SubscriberInterface { @@ -37,6 +38,8 @@ final class MokoOG extends CMSPlugin implements SubscriberInterface return [ 'onAfterRoute' => 'onAfterRoute', 'onBeforeCompileHead' => 'onBeforeCompileHead', + 'onContentAfterSave' => 'onContentAfterSaveRebuildSitemap', + 'onAjaxMokoog' => 'onAjaxMokoog', ]; } @@ -156,7 +159,10 @@ final class MokoOG extends CMSPlugin implements SubscriberInterface $doc->setMetaData('twitter:description', $description); if ($image) { - $doc->setMetaData('twitter:image', $this->resolveImageUrl($image)); + $twitterImage = ($this->params->get('auto_resize', 1) && $this->params->get('platform_resize', 0)) + ? ImageHelper::resizeForPlatform($image, 'twitter') + : $image; + $doc->setMetaData('twitter:image', $this->resolveImageUrl($twitterImage)); } if ($twitterSite) { @@ -346,6 +352,21 @@ final class MokoOG extends CMSPlugin implements SubscriberInterface } } + // Custom JSON-LD schema (user-provided) + $customSchema = $ogData->custom_schema ?? ''; + + if (!empty($customSchema)) { + $decoded = json_decode($customSchema, true); + + if ($decoded) { + if (empty($decoded['@context'])) { + $decoded['@context'] = 'https://schema.org'; + } + + $doc->addCustomTag(JsonLdBuilder::toScriptTag($decoded)); + } + } + if ($this->params->get('jsonld_breadcrumbs', 1)) { $breadcrumbs = JsonLdBuilder::buildBreadcrumbs(); @@ -428,6 +449,7 @@ final class MokoOG extends CMSPlugin implements SubscriberInterface 'og_video' => '', 'event_data' => '', 'recipe_data' => '', + 'custom_schema' => '', 'seo_title' => '', 'meta_description' => '', 'robots' => '', @@ -789,6 +811,129 @@ final class MokoOG extends CMSPlugin implements SubscriberInterface return $steps; } + /** + * Rebuild sitemap.xml when article content is saved. + * + * @param Event $event The event + * + * @return void + */ + public function onContentAfterSaveRebuildSitemap(Event $event): void + { + if (!$this->params->get('sitemap_enabled', 0)) { + return; + } + + [$context] = array_values($event->getArguments()); + + if ($context !== 'com_content.article') { + return; + } + + $changefreq = $this->params->get('sitemap_changefreq', 'weekly'); + $xml = SitemapBuilder::generate($changefreq); + SitemapBuilder::writeToFile($xml); + } + + /** + * Handle AJAX requests for AI meta tag generation. + * + * @param Event $event The event + * + * @return void + */ + public function onAjaxMokoog(Event $event): void + { + $app = $this->getApplication(); + + if (!$app->isClient('administrator')) { + return; + } + + \Joomla\CMS\Session\Session::checkToken() or die('Invalid Token'); + + if (!$this->params->get('ai_enabled', 0)) { + $event->setArgument('result', ['AI generation is not enabled']); + return; + } + + $apiKey = $this->params->get('ai_api_key', ''); + $provider = $this->params->get('ai_provider', 'claude'); + $model = $this->params->get('ai_model', 'claude-haiku-4-5-20251001'); + + if (empty($apiKey)) { + $event->setArgument('result', ['API key not configured']); + return; + } + + $input = $app->getInput(); + $field = $input->getString('field', 'title'); + $articleTitle = $input->getString('article_title', ''); + + $prompt = $field === 'title' + ? "Generate a concise, engaging social media sharing title (max 60 characters) for an article titled: \"$articleTitle\". Return only the title text, no quotes or explanation." + : "Generate a compelling social media sharing description (max 155 characters) for an article titled: \"$articleTitle\". Return only the description text, no quotes or explanation."; + + try { + $result = $this->callAiApi($provider, $apiKey, $model, $prompt); + $event->setArgument('result', [$result]); + } catch (\Exception $e) { + $event->setArgument('result', ['Error: ' . $e->getMessage()]); + } + } + + /** + * Call an AI API (Claude or OpenAI) with a prompt. + * + * @param string $provider Provider name (claude or openai) + * @param string $apiKey API key + * @param string $model Model name + * @param string $prompt Prompt text + * + * @return string Generated text + */ + private function callAiApi(string $provider, string $apiKey, string $model, string $prompt): string + { + $http = \Joomla\CMS\Http\HttpFactory::getHttp(); + + if ($provider === 'claude') { + $response = $http->post( + 'https://api.anthropic.com/v1/messages', + json_encode([ + 'model' => $model, + 'max_tokens' => 200, + 'messages' => [['role' => 'user', 'content' => $prompt]], + ]), + [ + 'Content-Type' => 'application/json', + 'x-api-key' => $apiKey, + 'anthropic-version' => '2023-06-01', + ] + ); + + $data = json_decode($response->body, true); + + return trim($data['content'][0]['text'] ?? ''); + } + + $response = $http->post( + 'https://api.openai.com/v1/chat/completions', + json_encode([ + 'model' => $model, + 'max_tokens' => 200, + 'messages' => [['role' => 'user', 'content' => $prompt]], + ]), + [ + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer ' . $apiKey, + ] + ); + + $data = json_decode($response->body, true); + + return trim($data['choices'][0]['message']['content'] ?? ''); + } + /** * Warn administrators once per session when no license key is configured. * diff --git a/source/packages/plg_system_mokoog/src/Helper/ImageHelper.php b/source/packages/plg_system_mokoog/src/Helper/ImageHelper.php index 6bdca69..2aaf5e9 100644 --- a/source/packages/plg_system_mokoog/src/Helper/ImageHelper.php +++ b/source/packages/plg_system_mokoog/src/Helper/ImageHelper.php @@ -149,6 +149,137 @@ class ImageHelper return $outputRel; } + /** + * Resize an image for a specific platform. + * + * @param string $imagePath Relative image path + * @param string $platform Platform name (facebook, twitter, pinterest, whatsapp) + * + * @return string Path to the resized image + */ + public static function resizeForPlatform(string $imagePath, string $platform): string + { + $sizes = [ + 'facebook' => ['width' => 1200, 'height' => 630], + 'twitter' => ['width' => 1200, 'height' => 600], + 'pinterest' => ['width' => 1000, 'height' => 1500], + 'whatsapp' => ['width' => 400, 'height' => 400], + ]; + + if (!isset($sizes[$platform])) { + return self::resize($imagePath); + } + + $size = $sizes[$platform]; + + return self::resizeToSize($imagePath, $size['width'], $size['height'], $platform); + } + + /** + * Resize an image to specific dimensions with a platform-specific subdirectory. + * + * @param string $imagePath Image path relative to JPATH_ROOT + * @param int $width Target width + * @param int $height Target height + * @param string $subdir Subdirectory name for output (e.g. platform name) + * + * @return string Path to the output image (relative to JPATH_ROOT) + */ + private static function resizeToSize(string $imagePath, int $width, int $height, string $subdir = ''): string + { + // Resolve absolute path + $absPath = JPATH_ROOT . '/' . ltrim($imagePath, '/'); + + if (!is_file($absPath)) { + return $imagePath; + } + + $imageInfo = getimagesize($absPath); + + if (!$imageInfo) { + Log::add('MokoOG ImageHelper: Cannot read image dimensions: ' . basename($absPath), Log::WARNING, 'mokoog'); + + return $imagePath; + } + + [$origWidth, $origHeight, $type] = $imageInfo; + + // Skip if already at or below target size + if ($origWidth <= $width && $origHeight <= $height) { + return $imagePath; + } + + // Build output directory with optional subdirectory + $outputRelDir = self::OUTPUT_DIR . ($subdir ? '/' . $subdir : ''); + $outputDir = JPATH_ROOT . '/' . $outputRelDir; + + if (!is_dir($outputDir) && !Folder::create($outputDir)) { + Log::add('MokoOG ImageHelper: Cannot create output directory: ' . $outputRelDir, Log::WARNING, 'mokoog'); + + return $imagePath; + } + + // Generate output filename based on source hash + dimensions + $hash = md5($imagePath . $width . $height); + $outputName = $hash . '.jpg'; + $outputPath = $outputDir . '/' . $outputName; + $outputRel = $outputRelDir . '/' . $outputName; + + // Skip if already generated + if (is_file($outputPath) && filemtime($outputPath) >= filemtime($absPath)) { + return $outputRel; + } + + // Load source image + $source = self::loadImage($absPath, $type); + + if (!$source) { + return $imagePath; + } + + // Calculate crop dimensions (center crop to target aspect ratio) + $targetRatio = $width / $height; + $sourceRatio = $origWidth / $origHeight; + + if ($sourceRatio > $targetRatio) { + // Source is wider — crop sides + $cropHeight = $origHeight; + $cropWidth = (int) round($origHeight * $targetRatio); + $cropX = (int) round(($origWidth - $cropWidth) / 2); + $cropY = 0; + } else { + // Source is taller — crop top/bottom + $cropWidth = $origWidth; + $cropHeight = (int) round($origWidth / $targetRatio); + $cropX = 0; + $cropY = (int) round(($origHeight - $cropHeight) / 2); + } + + // Create output canvas and resample + $output = imagecreatetruecolor($width, $height); + + imagecopyresampled( + $output, + $source, + 0, + 0, + $cropX, + $cropY, + $width, + $height, + $cropWidth, + $cropHeight + ); + + // Save as JPEG + imagejpeg($output, $outputPath, self::JPEG_QUALITY); + + imagedestroy($source); + imagedestroy($output); + + return $outputRel; + } + /** * Remove a generated image file. * diff --git a/source/packages/plg_system_mokoog/src/Helper/SitemapBuilder.php b/source/packages/plg_system_mokoog/src/Helper/SitemapBuilder.php new file mode 100644 index 0000000..70068e7 --- /dev/null +++ b/source/packages/plg_system_mokoog/src/Helper/SitemapBuilder.php @@ -0,0 +1,107 @@ +<?php + +/** + * @package MokoSuiteOpenGraph + * @subpackage plg_system_mokoog + * @author Moko Consulting <hello@mokoconsulting.tech> + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +namespace Joomla\Plugin\System\MokoOG\Helper; + +defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use Joomla\CMS\Uri\Uri; + +/** + * XML Sitemap builder. + * + * Generates a sitemap.xml containing all published articles, excluding + * those marked with noindex robots directives in the mokoog_tags table. + */ +class SitemapBuilder +{ + /** + * Generate sitemap XML content. + * + * @param string $changefreq Default change frequency for entries + * + * @return string Complete sitemap XML + */ + public static function generate(string $changefreq = 'weekly'): string + { + $db = Factory::getDbo(); + + // Get all published articles + $query = $db->getQuery(true) + ->select($db->quoteName(['a.id', 'a.alias', 'a.catid', 'a.modified', 'a.language'])) + ->from($db->quoteName('#__content', 'a')) + ->where($db->quoteName('a.state') . ' = 1'); + + $db->setQuery($query); + $articles = $db->loadObjectList(); + + // Get noindex articles from mokoog_tags + $noindexQuery = $db->getQuery(true) + ->select($db->quoteName('content_id')) + ->from($db->quoteName('#__mokoog_tags')) + ->where($db->quoteName('content_type') . ' = ' . $db->quote('com_content')) + ->where($db->quoteName('robots') . ' LIKE ' . $db->quote('%noindex%')); + + $db->setQuery($noindexQuery); + $noindexIds = $db->loadColumn(); + + $root = rtrim(Uri::root(), '/'); + $xml = '<?xml version="1.0" encoding="UTF-8"?>' . "\n"; + $xml .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n"; + + // Homepage + $xml .= ' <url>' . "\n"; + $xml .= ' <loc>' . $root . '/</loc>' . "\n"; + $xml .= ' <changefreq>daily</changefreq>' . "\n"; + $xml .= ' <priority>1.0</priority>' . "\n"; + $xml .= ' </url>' . "\n"; + + foreach ($articles as $article) { + // Skip noindexed + if (in_array((int) $article->id, $noindexIds)) { + continue; + } + + $url = $root . '/index.php?option=com_content&view=article&id=' . $article->id; + $lastmod = $article->modified && $article->modified !== '0000-00-00 00:00:00' + ? date('Y-m-d', strtotime($article->modified)) : ''; + + $xml .= ' <url>' . "\n"; + $xml .= ' <loc>' . htmlspecialchars($url, ENT_XML1) . '</loc>' . "\n"; + + if ($lastmod) { + $xml .= ' <lastmod>' . $lastmod . '</lastmod>' . "\n"; + } + + $xml .= ' <changefreq>' . $changefreq . '</changefreq>' . "\n"; + $xml .= ' <priority>0.8</priority>' . "\n"; + $xml .= ' </url>' . "\n"; + } + + $xml .= '</urlset>'; + + return $xml; + } + + /** + * Write sitemap XML to the site root. + * + * @param string $xml The sitemap XML content + * + * @return bool True on success + */ + public static function writeToFile(string $xml): bool + { + $path = JPATH_ROOT . '/sitemap.xml'; + + return (bool) file_put_contents($path, $xml); + } +} From bc6ce4397f181aff53d0db6188d9b52f0c40fea1 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" <gitea-actions[bot]@mokoconsulting.tech> Date: Tue, 23 Jun 2026 17:54:31 +0000 Subject: [PATCH 17/41] chore(version): auto-bump patch 01.04.04-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 2 +- README.md | 2 +- source/packages/com_mokoog/mokoog.xml | 2 +- source/packages/plg_content_mokoog/mokoog.xml | 2 +- source/packages/plg_system_mokoog/mokoog.xml | 2 +- source/packages/plg_webservices_mokoog/mokoog.xml | 2 +- source/pkg_mokoog.xml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 5de1024..eca3a7b 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.04.03 +# VERSION: 01.04.04 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index 75e0dc2..236677d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -<!-- VERSION: 01.04.03 --> +<!-- VERSION: 01.04.04 --> All notable changes to MokoSuiteOpenGraph will be documented in this file. diff --git a/README.md b/README.md index b2f8ee2..c30ff93 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoSuiteOpenGraph -<!-- VERSION: 01.04.03 --> +<!-- VERSION: 01.04.04 --> Open Graph, Twitter Card, and social sharing meta tag management for Joomla 4/5/6. diff --git a/source/packages/com_mokoog/mokoog.xml b/source/packages/com_mokoog/mokoog.xml index 0503799..46bea6d 100644 --- a/source/packages/com_mokoog/mokoog.xml +++ b/source/packages/com_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="component" method="upgrade"> <name>com_mokoog</name> - <version>01.04.03</version> + <version>01.04.04</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_content_mokoog/mokoog.xml b/source/packages/plg_content_mokoog/mokoog.xml index bc876fe..5337293 100644 --- a/source/packages/plg_content_mokoog/mokoog.xml +++ b/source/packages/plg_content_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="content" method="upgrade"> <name>Content - MokoJoomOpenGraph</name> - <version>01.04.03</version> + <version>01.04.04</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_system_mokoog/mokoog.xml b/source/packages/plg_system_mokoog/mokoog.xml index aa97e32..b745f19 100644 --- a/source/packages/plg_system_mokoog/mokoog.xml +++ b/source/packages/plg_system_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="system" method="upgrade"> <name>System - MokoJoomOpenGraph</name> - <version>01.04.03</version> + <version>01.04.04</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_webservices_mokoog/mokoog.xml b/source/packages/plg_webservices_mokoog/mokoog.xml index 8b05cff..e9857e9 100644 --- a/source/packages/plg_webservices_mokoog/mokoog.xml +++ b/source/packages/plg_webservices_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="webservices" method="upgrade"> <name>Web Services - MokoJoomOpenGraph</name> - <version>01.04.03</version> + <version>01.04.04</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/pkg_mokoog.xml b/source/pkg_mokoog.xml index 03198ac..79e2885 100644 --- a/source/pkg_mokoog.xml +++ b/source/pkg_mokoog.xml @@ -8,7 +8,7 @@ <extension type="package" method="upgrade"> <name>Package - MokoSuiteOpenGraph</name> <packagename>mokoog</packagename> - <version>01.04.03</version> + <version>01.04.04</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> From cf783c6b83a8485e652a85516b1becb86bdd1975 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" <gitea-actions[bot]@mokoconsulting.tech> Date: Tue, 23 Jun 2026 17:54:40 +0000 Subject: [PATCH 18/41] chore(version): pre-release bump to 01.04.05-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 2 +- README.md | 2 +- source/packages/com_mokoog/mokoog.xml | 2 +- source/packages/plg_content_mokoog/mokoog.xml | 2 +- source/packages/plg_system_mokoog/mokoog.xml | 2 +- source/packages/plg_webservices_mokoog/mokoog.xml | 2 +- source/pkg_mokoog.xml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index eca3a7b..2ce216e 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.04.04 +# VERSION: 01.04.05 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index 236677d..88b07d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -<!-- VERSION: 01.04.04 --> +<!-- VERSION: 01.04.05 --> All notable changes to MokoSuiteOpenGraph will be documented in this file. diff --git a/README.md b/README.md index c30ff93..744c839 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoSuiteOpenGraph -<!-- VERSION: 01.04.04 --> +<!-- VERSION: 01.04.05 --> Open Graph, Twitter Card, and social sharing meta tag management for Joomla 4/5/6. diff --git a/source/packages/com_mokoog/mokoog.xml b/source/packages/com_mokoog/mokoog.xml index 46bea6d..0e2bb79 100644 --- a/source/packages/com_mokoog/mokoog.xml +++ b/source/packages/com_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="component" method="upgrade"> <name>com_mokoog</name> - <version>01.04.04</version> + <version>01.04.05</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_content_mokoog/mokoog.xml b/source/packages/plg_content_mokoog/mokoog.xml index 5337293..40a254b 100644 --- a/source/packages/plg_content_mokoog/mokoog.xml +++ b/source/packages/plg_content_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="content" method="upgrade"> <name>Content - MokoJoomOpenGraph</name> - <version>01.04.04</version> + <version>01.04.05</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_system_mokoog/mokoog.xml b/source/packages/plg_system_mokoog/mokoog.xml index b745f19..6d543d3 100644 --- a/source/packages/plg_system_mokoog/mokoog.xml +++ b/source/packages/plg_system_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="system" method="upgrade"> <name>System - MokoJoomOpenGraph</name> - <version>01.04.04</version> + <version>01.04.05</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_webservices_mokoog/mokoog.xml b/source/packages/plg_webservices_mokoog/mokoog.xml index e9857e9..8ea5b63 100644 --- a/source/packages/plg_webservices_mokoog/mokoog.xml +++ b/source/packages/plg_webservices_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="webservices" method="upgrade"> <name>Web Services - MokoJoomOpenGraph</name> - <version>01.04.04</version> + <version>01.04.05</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/pkg_mokoog.xml b/source/pkg_mokoog.xml index 79e2885..109130a 100644 --- a/source/pkg_mokoog.xml +++ b/source/pkg_mokoog.xml @@ -8,7 +8,7 @@ <extension type="package" method="upgrade"> <name>Package - MokoSuiteOpenGraph</name> <packagename>mokoog</packagename> - <version>01.04.04</version> + <version>01.04.05</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> From a578ac3bb3cdfeeaa68651c7b7fbc203eddbacba Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Tue, 23 Jun 2026 18:00:09 +0000 Subject: [PATCH 19/41] chore: remove deploy-manual.yml -- no longer needed --- .mokogitea/workflows/deploy-manual.yml | 126 ------------------------- 1 file changed, 126 deletions(-) delete mode 100644 .mokogitea/workflows/deploy-manual.yml diff --git a/.mokogitea/workflows/deploy-manual.yml b/.mokogitea/workflows/deploy-manual.yml deleted file mode 100644 index bb133ed..0000000 --- a/.mokogitea/workflows/deploy-manual.yml +++ /dev/null @@ -1,126 +0,0 @@ -# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech> -# -# SPDX-License-Identifier: GPL-3.0-or-later -# -# FILE INFORMATION -# DEFGROUP: Gitea.Workflow -# INGROUP: MokoStandards.Deploy -# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards-API -# PATH: /templates/workflows/joomla/deploy-manual.yml.template -# VERSION: 04.07.00 -# BRIEF: Manual SFTP deploy to dev server for Joomla repos - -name: "Universal: Deploy to Dev (Manual)" - -on: - workflow_dispatch: - inputs: - clear_remote: - description: 'Delete all remote files before uploading' - required: false - default: 'false' - type: boolean - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - -permissions: - contents: read - -jobs: - deploy: - name: SFTP Deploy to Dev - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - - name: Setup PHP - run: | - php -v && composer --version - - - name: Setup MokoStandards tools - env: - GA_TOKEN: ${{ secrets.GA_TOKEN || secrets.GA_TOKEN || github.token }} - MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN || secrets.GA_TOKEN || github.token }} - MOKO_CLONE_HOST: ${{ secrets.GA_TOKEN && 'git.mokoconsulting.tech/MokoConsulting' || 'github.com/mokoconsulting-tech' }} - COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GA_TOKEN || github.token }}"}}' - run: | - git clone --depth 1 --branch main --quiet \ - "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/MokoStandards-API.git" \ - /tmp/mokostandards-api 2>/dev/null || true - if [ -d "/tmp/mokostandards-api" ] && [ -f "/tmp/mokostandards-api/composer.json" ]; then - cd /tmp/mokostandards-api && composer install --no-dev --no-interaction --quiet 2>/dev/null || true - fi - - - name: Check FTP configuration - id: check - env: - HOST: ${{ vars.DEV_FTP_HOST }} - PATH_VAR: ${{ vars.DEV_FTP_PATH }} - PORT: ${{ vars.DEV_FTP_PORT }} - run: | - if [ -z "$HOST" ] || [ -z "$PATH_VAR" ]; then - echo "DEV_FTP_HOST or DEV_FTP_PATH not configured -- cannot deploy" - echo "skip=true" >> "$GITHUB_OUTPUT" - exit 0 - fi - echo "skip=false" >> "$GITHUB_OUTPUT" - echo "host=$HOST" >> "$GITHUB_OUTPUT" - - REMOTE="${PATH_VAR%/}" - echo "remote=$REMOTE" >> "$GITHUB_OUTPUT" - - [ -z "$PORT" ] && PORT="22" - echo "port=$PORT" >> "$GITHUB_OUTPUT" - - - name: Deploy via SFTP - if: steps.check.outputs.skip != 'true' - env: - SFTP_KEY: ${{ secrets.DEV_FTP_KEY }} - SFTP_PASS: ${{ secrets.DEV_FTP_PASSWORD }} - SFTP_USER: ${{ vars.DEV_FTP_USERNAME }} - run: | - SOURCE_DIR="src" - [ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs" - [ ! -d "$SOURCE_DIR" ] && { echo "No src/ or htdocs/ -- nothing to deploy"; exit 0; } - - printf '{"host":"%s","port":%s,"username":"%s","remotePath":"%s"' \ - "${{ steps.check.outputs.host }}" "${{ steps.check.outputs.port }}" "$SFTP_USER" "${{ steps.check.outputs.remote }}" \ - > /tmp/sftp-config.json - - if [ -n "$SFTP_KEY" ]; then - echo "$SFTP_KEY" > /tmp/deploy_key - chmod 600 /tmp/deploy_key - printf ',"privateKeyPath":"/tmp/deploy_key"}' >> /tmp/sftp-config.json - else - printf ',"password":"%s"}' "$SFTP_PASS" >> /tmp/sftp-config.json - fi - - DEPLOY_ARGS=(--path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json) - [ "${{ inputs.clear_remote }}" = "true" ] && DEPLOY_ARGS+=(--clear-remote) - - PLATFORM=$(php /tmp/mokostandards-api/cli/platform_detect.php --path . 2>/dev/null || true) - if [ "$PLATFORM" = "waas-component" ] && [ -f "/tmp/mokostandards-api/deploy/deploy-joomla.php" ]; then - php /tmp/mokostandards-api/deploy/deploy-joomla.php "${DEPLOY_ARGS[@]}" - else - php /tmp/mokostandards-api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}" - fi - - rm -f /tmp/deploy_key /tmp/sftp-config.json - - - name: Summary - if: always() - run: | - if [ "${{ steps.check.outputs.skip }}" = "true" ]; then - echo "### Deploy Skipped -- FTP not configured" >> $GITHUB_STEP_SUMMARY - else - echo "### Manual Dev Deploy Complete" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY - echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY - echo "| Host | \`${{ steps.check.outputs.host }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Remote | \`${{ steps.check.outputs.remote }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Clear | ${{ inputs.clear_remote }} |" >> $GITHUB_STEP_SUMMARY - fi From f1c6eb8f6e4d726d5e323e7a5c3e0657a92002fc Mon Sep 17 00:00:00 2001 From: Jonathan Miller <jmiller-moko@noreply.git.mokoconsulting.tech> Date: Tue, 23 Jun 2026 13:05:15 -0500 Subject: [PATCH 20/41] docs: update CHANGELOG and README for v2.0 release Add all v2.0 features: 11+ JSON-LD types, 6 preview cards, AI generation, XML sitemap, per-platform images, SEO scoring, PHPUnit tests, OpenAPI spec, coverage dashboard, custom schema builder. --- CHANGELOG.md | 25 +++++++++++++++++++++++++ README.md | 20 ++++++++++++++++++-- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88b07d6..c66cb6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,21 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - LinkedIn social preview card in article/menu editor alongside Facebook and Twitter/X previews (#61) - `og:video` meta tag support with per-article video URL field, auto-detect MIME type for YouTube/Vimeo/direct files (#59) - Pinterest rich pin tags: `article:tag` from Joomla content tags, `product:availability` from MokoSuiteShop stock (#60) +- FAQ JSON-LD schema with auto-detection from article h3/h4 headings (#62) +- HowTo JSON-LD schema with auto-detection from ordered lists (#63) +- Event JSON-LD schema with per-article event fields (dates, venue, tickets) (#64) +- LocalBusiness JSON-LD schema with global plugin configuration (#65) +- Recipe JSON-LD schema with per-article fields (times, ingredients, nutrition) (#66) +- VideoObject JSON-LD schema for articles with video URLs (#67) +- SEO content scoring panel with 7 checks and pass/fail indicators (#68) +- Discord, Mastodon, and Slack social preview cards in editor (#69) +- Custom JSON-LD schema builder — per-article textarea for any schema.org type (#70) +- AI-powered meta tag generation with Claude and OpenAI API support (#71) +- XML sitemap generation on article save, respects noindex directives (#72) +- OG coverage dashboard in tag manager with coverage percentage (#73) +- Per-platform image resizing: Twitter 1200x600, Pinterest 1000x1500, WhatsApp 400x400 (#74) +- PHPUnit test suite with 16 unit tests for JsonLdBuilder (#75) +- OpenAPI 3.0 specification for REST API (#80) - 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` @@ -45,6 +60,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Facebook App ID and Telegram channel support - Database table `#__mokoog_tags` with multilingual unique key +### Fixed +- Add exception logging to BatchController batch skip (#76) +- Align form maxlength attributes with DB schema limits (#77) +- Add `strip_tags()` input sanitization on OG text fields (#79) +- Only emit `og:video:secure_url` for HTTPS URLs +- Only emit `og:video:width/height` for direct files, not embeds +- Consolidate duplicate MokoSuiteShop product blocks +- Fix stale `com_virtuemart` reference in SQL comment +- Use component language keys for og_video field in tag.xml + ### Changed - Consolidated article DB queries into single cached lookup — 5 queries reduced to 1 (#38) - Dynamic `og:image:width`/`og:image:height` from actual image dimensions instead of hardcoded (#39) diff --git a/README.md b/README.md index 744c839..c9d5117 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,9 @@ MokoSuiteOpenGraph gives you full control over how your Joomla content appears w - **LinkedIn** — `article:published_time`, `article:modified_time`, `article:author` - **Discord** — Custom embed color via `theme-color` meta tag - **Telegram** — `telegram:channel` for link previews +- **Mastodon/Fediverse** — `fediverse:creator` for author attribution (first extension on any CMS) +- **Pinterest** — Rich pin tags: `article:tag`, `product:availability`, `product:price` +- **og:video** — Per-article video URLs with auto MIME type detection (YouTube/Vimeo/direct) - **Facebook** — `fb:app_id` support, `og:image:width`/`og:image:height` for instant previews ### Content Management @@ -31,7 +34,8 @@ MokoSuiteOpenGraph gives you full control over how your Joomla content appears w - **Meta description** — Per-page meta description control - **Robots directive** — Per-page noindex/nofollow settings - **Canonical URL** — Custom canonical URL overrides -- **JSON-LD structured data** — Article, Product, WebPage, BreadcrumbList, Organization schemas +- **JSON-LD structured data** — Article, Product, WebPage, BreadcrumbList, Organization, FAQ, HowTo, Event, Recipe, LocalBusiness, VideoObject, and custom schemas +- **SEO content scoring** — 7-check analysis panel with pass/fail indicators in the editor ### Admin Tools - **Tag manager dashboard** — View and manage all OG records centrally @@ -39,13 +43,20 @@ MokoSuiteOpenGraph gives you full control over how your Joomla content appears w - **CSV import/export** — Bulk manage OG data via CSV files - **SEO health badges** — Visual indicators for missing descriptions, long titles, noindex - **Debug links** — Quick links to Facebook Debugger, LinkedIn Inspector, Google Rich Results -- **Live preview** — Real-time Facebook and Twitter/X card preview in the editor +- **Live preview** — Real-time Facebook, Twitter/X, LinkedIn, Discord, Mastodon, and Slack card previews in the editor +- **Character count indicators** — Green/yellow/red warnings on OG and SEO text fields +- **OG coverage dashboard** — Coverage percentage and missing field counts +- **AI meta generation** — Generate OG titles and descriptions with Claude or OpenAI ### Developer Features - **REST API** — Full CRUD via Joomla Web Services (`/api/v1/mokoog/tags`) - **MokoSuiteShop integration** — Auto-generated OG/JSON-LD for product pages with pricing meta - **Plugin event** — `onMokoOGAfterRender` for third-party plugins to add custom social tags - **OG image generator** — Text overlay on template backgrounds with auto-resize to 1200x630 +- **Per-platform image resizing** — Twitter 1200x600, Pinterest 1000x1500, WhatsApp 400x400 +- **XML sitemap** — Auto-generates sitemap.xml on article save, respects noindex +- **OpenAPI spec** — Full REST API documentation at `openapi.yaml` +- **PHPUnit tests** — 16 unit tests for JsonLdBuilder schema outputs ## Installation @@ -63,6 +74,11 @@ Navigate to **Extensions → Plugins → System - MokoSuiteOpenGraph** to config - Facebook App ID - Discord embed color - Telegram channel +- Fediverse/Mastodon creator handle +- LocalBusiness schema (address, phone, hours, geo) +- XML sitemap generation +- AI meta generation (Claude/OpenAI API key) +- Per-platform image resizing - Auto-generation, image resize, JSON-LD, and description length settings ## License From a9fc5d2cf18450498e6dc892c841b73fc378db92 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Tue, 23 Jun 2026 18:05:38 +0000 Subject: [PATCH 21/41] chore: remove security-audit.yml -- handled by MokoGitea --- .mokogitea/workflows/security-audit.yml | 82 ------------------------- 1 file changed, 82 deletions(-) delete mode 100644 .mokogitea/workflows/security-audit.yml diff --git a/.mokogitea/workflows/security-audit.yml b/.mokogitea/workflows/security-audit.yml deleted file mode 100644 index 789325a..0000000 --- a/.mokogitea/workflows/security-audit.yml +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech> -# -# SPDX-License-Identifier: GPL-3.0-or-later -# -# FILE INFORMATION -# DEFGROUP: Gitea.Workflow -# INGROUP: MokoStandards.Security -# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards -# PATH: /.gitea/workflows/security-audit.yml -# VERSION: 01.00.00 -# BRIEF: Dependency vulnerability scanning for composer and npm packages - -name: "Universal: Security Audit" - -on: - schedule: - - cron: '0 6 * * 1' # Weekly on Monday at 06:00 UTC - pull_request: - branches: - - main - paths: - - 'composer.json' - - 'composer.lock' - - 'package.json' - - 'package-lock.json' - workflow_dispatch: - -permissions: - contents: read - -env: - NTFY_URL: ${{ vars.NTFY_URL || 'https://ntfy.mokoconsulting.tech' }} - NTFY_TOPIC: ${{ vars.NTFY_TOPIC || 'gitea-security' }} - -jobs: - audit: - name: Dependency Audit - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Composer audit - if: hashFiles('composer.lock') != '' - run: | - echo "=== Composer Security Audit ===" - if ! command -v composer &> /dev/null; then - sudo apt-get update -qq - sudo apt-get install -y -qq php-cli composer >/dev/null 2>&1 - fi - composer audit --format=plain 2>&1 | tee /tmp/composer-audit.txt - RESULT=$? - if [ $RESULT -ne 0 ]; then - echo "::warning::Composer vulnerabilities found" - echo "composer_vulnerable=true" >> "$GITHUB_ENV" - else - echo "No known vulnerabilities in composer dependencies" - fi - - - name: NPM audit - if: hashFiles('package-lock.json') != '' - run: | - echo "=== NPM Security Audit ===" - npm audit --production 2>&1 | tee /tmp/npm-audit.txt || true - if npm audit --production 2>&1 | grep -q "found 0 vulnerabilities"; then - echo "No known vulnerabilities in npm dependencies" - else - echo "::warning::NPM vulnerabilities found" - echo "npm_vulnerable=true" >> "$GITHUB_ENV" - fi - - - name: Notify on vulnerabilities - if: env.composer_vulnerable == 'true' || env.npm_vulnerable == 'true' - run: | - REPO="${{ github.event.repository.name }}" - curl -sS \ - -H "Title: ${REPO} has vulnerable dependencies" \ - -H "Tags: lock,warning" \ - -H "Priority: high" \ - -d "Security audit found vulnerabilities. Review dependency updates." \ - "${NTFY_URL}/${NTFY_TOPIC}" || true From 9793bd4031df7b62daee173d068a33c1fb428039 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" <gitea-actions[bot]@mokoconsulting.tech> Date: Tue, 23 Jun 2026 18:07:08 +0000 Subject: [PATCH 22/41] chore(version): pre-release bump to 01.04.06-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 2 +- README.md | 2 +- source/packages/com_mokoog/mokoog.xml | 2 +- source/packages/plg_content_mokoog/mokoog.xml | 2 +- source/packages/plg_system_mokoog/mokoog.xml | 2 +- source/packages/plg_webservices_mokoog/mokoog.xml | 2 +- source/pkg_mokoog.xml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 2ce216e..356b307 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.04.05 +# VERSION: 01.04.06 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index c66cb6a..2875bf8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -<!-- VERSION: 01.04.05 --> +<!-- VERSION: 01.04.06 --> All notable changes to MokoSuiteOpenGraph will be documented in this file. diff --git a/README.md b/README.md index c9d5117..44b6ba4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoSuiteOpenGraph -<!-- VERSION: 01.04.05 --> +<!-- VERSION: 01.04.06 --> Open Graph, Twitter Card, and social sharing meta tag management for Joomla 4/5/6. diff --git a/source/packages/com_mokoog/mokoog.xml b/source/packages/com_mokoog/mokoog.xml index 0e2bb79..24efa85 100644 --- a/source/packages/com_mokoog/mokoog.xml +++ b/source/packages/com_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="component" method="upgrade"> <name>com_mokoog</name> - <version>01.04.05</version> + <version>01.04.06</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_content_mokoog/mokoog.xml b/source/packages/plg_content_mokoog/mokoog.xml index 40a254b..3929f11 100644 --- a/source/packages/plg_content_mokoog/mokoog.xml +++ b/source/packages/plg_content_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="content" method="upgrade"> <name>Content - MokoJoomOpenGraph</name> - <version>01.04.05</version> + <version>01.04.06</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_system_mokoog/mokoog.xml b/source/packages/plg_system_mokoog/mokoog.xml index 6d543d3..1cbfa33 100644 --- a/source/packages/plg_system_mokoog/mokoog.xml +++ b/source/packages/plg_system_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="system" method="upgrade"> <name>System - MokoJoomOpenGraph</name> - <version>01.04.05</version> + <version>01.04.06</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_webservices_mokoog/mokoog.xml b/source/packages/plg_webservices_mokoog/mokoog.xml index 8ea5b63..58e8203 100644 --- a/source/packages/plg_webservices_mokoog/mokoog.xml +++ b/source/packages/plg_webservices_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="webservices" method="upgrade"> <name>Web Services - MokoJoomOpenGraph</name> - <version>01.04.05</version> + <version>01.04.06</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/pkg_mokoog.xml b/source/pkg_mokoog.xml index 109130a..9fbbbfe 100644 --- a/source/pkg_mokoog.xml +++ b/source/pkg_mokoog.xml @@ -8,7 +8,7 @@ <extension type="package" method="upgrade"> <name>Package - MokoSuiteOpenGraph</name> <packagename>mokoog</packagename> - <version>01.04.05</version> + <version>01.04.06</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> From 8de243b1814f73e857aa03bd548d6b7f14d27a8f Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" <gitea-actions[bot]@mokoconsulting.tech> Date: Tue, 23 Jun 2026 18:21:17 +0000 Subject: [PATCH 23/41] chore(version): pre-release bump to 01.04.07-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 2 +- README.md | 2 +- source/packages/com_mokoog/mokoog.xml | 2 +- source/packages/plg_content_mokoog/mokoog.xml | 2 +- source/packages/plg_system_mokoog/mokoog.xml | 2 +- source/packages/plg_webservices_mokoog/mokoog.xml | 2 +- source/pkg_mokoog.xml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 356b307..38def9e 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.04.06 +# VERSION: 01.04.07 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index 2875bf8..c916adb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -<!-- VERSION: 01.04.06 --> +<!-- VERSION: 01.04.07 --> All notable changes to MokoSuiteOpenGraph will be documented in this file. diff --git a/README.md b/README.md index 44b6ba4..a3313c9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoSuiteOpenGraph -<!-- VERSION: 01.04.06 --> +<!-- VERSION: 01.04.07 --> Open Graph, Twitter Card, and social sharing meta tag management for Joomla 4/5/6. diff --git a/source/packages/com_mokoog/mokoog.xml b/source/packages/com_mokoog/mokoog.xml index 24efa85..110605f 100644 --- a/source/packages/com_mokoog/mokoog.xml +++ b/source/packages/com_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="component" method="upgrade"> <name>com_mokoog</name> - <version>01.04.06</version> + <version>01.04.07</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_content_mokoog/mokoog.xml b/source/packages/plg_content_mokoog/mokoog.xml index 3929f11..8621607 100644 --- a/source/packages/plg_content_mokoog/mokoog.xml +++ b/source/packages/plg_content_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="content" method="upgrade"> <name>Content - MokoJoomOpenGraph</name> - <version>01.04.06</version> + <version>01.04.07</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_system_mokoog/mokoog.xml b/source/packages/plg_system_mokoog/mokoog.xml index 1cbfa33..efa29b4 100644 --- a/source/packages/plg_system_mokoog/mokoog.xml +++ b/source/packages/plg_system_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="system" method="upgrade"> <name>System - MokoJoomOpenGraph</name> - <version>01.04.06</version> + <version>01.04.07</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_webservices_mokoog/mokoog.xml b/source/packages/plg_webservices_mokoog/mokoog.xml index 58e8203..a169943 100644 --- a/source/packages/plg_webservices_mokoog/mokoog.xml +++ b/source/packages/plg_webservices_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="webservices" method="upgrade"> <name>Web Services - MokoJoomOpenGraph</name> - <version>01.04.06</version> + <version>01.04.07</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/pkg_mokoog.xml b/source/pkg_mokoog.xml index 9fbbbfe..2aee709 100644 --- a/source/pkg_mokoog.xml +++ b/source/pkg_mokoog.xml @@ -8,7 +8,7 @@ <extension type="package" method="upgrade"> <name>Package - MokoSuiteOpenGraph</name> <packagename>mokoog</packagename> - <version>01.04.06</version> + <version>01.04.07</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> From 281e742b545cc37f4f09ea5a26941daeeef66042 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" <gitea-actions[bot]@mokoconsulting.tech> Date: Tue, 23 Jun 2026 18:28:10 +0000 Subject: [PATCH 24/41] chore(version): pre-release bump to 01.04.08-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 2 +- README.md | 2 +- source/packages/com_mokoog/mokoog.xml | 2 +- source/packages/plg_content_mokoog/mokoog.xml | 2 +- source/packages/plg_system_mokoog/mokoog.xml | 2 +- source/packages/plg_webservices_mokoog/mokoog.xml | 2 +- source/pkg_mokoog.xml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 38def9e..194bf30 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.04.07 +# VERSION: 01.04.08 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index c916adb..504615f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -<!-- VERSION: 01.04.07 --> +<!-- VERSION: 01.04.08 --> All notable changes to MokoSuiteOpenGraph will be documented in this file. diff --git a/README.md b/README.md index a3313c9..f4323db 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoSuiteOpenGraph -<!-- VERSION: 01.04.07 --> +<!-- VERSION: 01.04.08 --> Open Graph, Twitter Card, and social sharing meta tag management for Joomla 4/5/6. diff --git a/source/packages/com_mokoog/mokoog.xml b/source/packages/com_mokoog/mokoog.xml index 110605f..4bbfc08 100644 --- a/source/packages/com_mokoog/mokoog.xml +++ b/source/packages/com_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="component" method="upgrade"> <name>com_mokoog</name> - <version>01.04.07</version> + <version>01.04.08</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_content_mokoog/mokoog.xml b/source/packages/plg_content_mokoog/mokoog.xml index 8621607..9cb592a 100644 --- a/source/packages/plg_content_mokoog/mokoog.xml +++ b/source/packages/plg_content_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="content" method="upgrade"> <name>Content - MokoJoomOpenGraph</name> - <version>01.04.07</version> + <version>01.04.08</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_system_mokoog/mokoog.xml b/source/packages/plg_system_mokoog/mokoog.xml index efa29b4..b0f7ae3 100644 --- a/source/packages/plg_system_mokoog/mokoog.xml +++ b/source/packages/plg_system_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="system" method="upgrade"> <name>System - MokoJoomOpenGraph</name> - <version>01.04.07</version> + <version>01.04.08</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_webservices_mokoog/mokoog.xml b/source/packages/plg_webservices_mokoog/mokoog.xml index a169943..4094a1f 100644 --- a/source/packages/plg_webservices_mokoog/mokoog.xml +++ b/source/packages/plg_webservices_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="webservices" method="upgrade"> <name>Web Services - MokoJoomOpenGraph</name> - <version>01.04.07</version> + <version>01.04.08</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/pkg_mokoog.xml b/source/pkg_mokoog.xml index 2aee709..6a73a1f 100644 --- a/source/pkg_mokoog.xml +++ b/source/pkg_mokoog.xml @@ -8,7 +8,7 @@ <extension type="package" method="upgrade"> <name>Package - MokoSuiteOpenGraph</name> <packagename>mokoog</packagename> - <version>01.04.07</version> + <version>01.04.08</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> From d01b39841a3d4094f476f46d1caa03f1f2ab6d23 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sun, 28 Jun 2026 07:25:41 +0000 Subject: [PATCH 25/41] chore: add SECURITY.md from Template-Joomla --- SECURITY.md | 241 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 241 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..81c22ff --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,241 @@ +<!-- +Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech> + +This file is part of a Moko Consulting project. + +SPDX-License-Identifier: GPL-3.0-or-later + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <https://www.gnu.org/licenses/>. + +# FILE INFORMATION +DEFGROUP: Template-Joomla +INGROUP: Template-Joomla.Documentation +REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla +PATH: /SECURITY.md +VERSION: 01.01.00 +BRIEF: Security vulnerability reporting and handling policy +--> + +# Security Policy + +## Purpose and Scope + +This document defines the security vulnerability reporting, response, and disclosure policy for this Joomla Plugin template repository. It establishes the authoritative process for responsible disclosure, assessment, remediation, and communication of security issues. + +## Supported Versions + +Security updates are provided for the following versions: + +| Version | Supported | +| ------- | ------------------ | +| 01.x.x | :white_check_mark: | +| < 01.0 | :x: | + +Only the current major version receives security updates. Users should upgrade to the latest supported version to receive security patches. + +## Reporting a Vulnerability + +### Where to Report + +**DO NOT** create public GitHub issues for security vulnerabilities. + +Report security vulnerabilities privately to: + +**Email**: `security@mokoconsulting.tech` + +**Subject Line**: `[SECURITY] Template-Joomla - Brief Description` + +### What to Include + +A complete vulnerability report should include: + +1. **Description**: Clear explanation of the vulnerability +2. **Impact**: Potential security impact and severity assessment +3. **Affected Versions**: Which versions are vulnerable +4. **Reproduction Steps**: Detailed steps to reproduce the issue +5. **Proof of Concept**: Code, configuration, or demonstration (if applicable) +6. **Suggested Fix**: Proposed remediation (if known) +7. **Disclosure Timeline**: Your expectations for public disclosure + +### Response Timeline + +* **Initial Response**: Within 3 business days +* **Assessment Complete**: Within 7 business days +* **Fix Timeline**: Depends on severity (see below) +* **Disclosure**: Coordinated with reporter + +## Severity Classification + +Vulnerabilities are classified using the following severity levels: + +### Critical +* Remote code execution +* Authentication bypass +* Data breach or exposure of sensitive information +* **Fix Timeline**: 7 days + +### High +* Privilege escalation +* SQL injection or command injection +* Cross-site scripting (XSS) with significant impact +* **Fix Timeline**: 14 days + +### Medium +* Information disclosure (limited scope) +* Denial of service +* Security misconfigurations with moderate impact +* **Fix Timeline**: 30 days + +### Low +* Security best practice violations +* Minor information leaks +* Issues requiring user interaction or complex preconditions +* **Fix Timeline**: 60 days or next release + +## Remediation Process + +1. **Acknowledgment**: Security team confirms receipt and begins investigation +2. **Assessment**: Vulnerability is validated, severity assigned, and impact analyzed +3. **Development**: Security patch is developed and tested +4. **Review**: Patch undergoes security review and validation +5. **Release**: Fixed version is released with security advisory +6. **Disclosure**: Public disclosure follows coordinated timeline + +## Security Advisories + +Security advisories are published via: + +* GitHub Security Advisories +* Release notes and CHANGELOG.md +* Email notification to project users (if mailing list is established) + +Advisories include: + +* CVE identifier (if applicable) +* Severity rating +* Affected versions +* Fixed versions +* Mitigation steps +* Attribution (with reporter consent) + +## Security Best Practices + +For projects using this template: + +### Required Controls + +* Enable GitHub security features (Dependabot, code scanning) +* Implement branch protection on `main` +* Require code review for all changes +* Enforce signed commits (recommended) +* Use secrets management (never commit credentials) +* Maintain security documentation +* Follow secure coding standards defined in MokoStandards + +### Joomla Plugin Security + +* Follow Joomla security best practices +* Validate and sanitize all user input +* Use Joomla's database API to prevent SQL injection +* Properly escape output to prevent XSS +* Implement proper access control checks +* Use Joomla's session and authentication APIs +* Keep Joomla and dependencies up to date + +### CI/CD Security + +* Validate all inputs +* Sanitize outputs +* Use least privilege access +* Pin dependencies with hash verification +* Scan for vulnerabilities in dependencies +* Audit third-party actions and tools + +#### Automated Security Scanning + +All repositories SHOULD implement: + +**CodeQL Analysis**: +* Enabled for PHP and other supported languages +* Runs on: push to main, pull requests, weekly schedule +* Query sets: `security-extended` and `security-and-quality` +* Configuration: `.github/workflows/codeql-analysis.yml` + +**Dependabot Security Updates**: +* Weekly scans for vulnerable dependencies +* Automated pull requests for security patches +* Configuration: `.github/dependabot.yml` + +**Secret Scanning**: +* Enabled by default with push protection +* Prevents accidental credential commits + +### Dependency Management + +* Keep dependencies up to date +* Monitor security advisories for dependencies +* Remove unused dependencies +* Audit new dependencies before adoption +* Document security-critical dependencies + +## Compliance and Governance + +This security policy is aligned with MokoStandards. Deviations require documented justification. + +Security policies are reviewed and updated at least annually or following significant security incidents. + +## Attribution and Recognition + +We acknowledge and appreciate responsible disclosure. With your permission, we will: + +* Credit you in security advisories +* List you in CHANGELOG.md for the fix release +* Recognize your contribution publicly (if desired) + +## Contact and Escalation + +* **Security Team**: security@mokoconsulting.tech +* **Primary Contact**: hello@mokoconsulting.tech +* **Escalation**: For urgent matters requiring immediate attention, contact the maintainer directly via GitHub + +## Out of Scope + +The following are explicitly out of scope: + +* Issues in third-party dependencies (report directly to maintainers) +* Social engineering attacks +* Physical security issues +* Denial of service via resource exhaustion without amplification +* Issues requiring physical access to systems +* Theoretical vulnerabilities without proof of exploitability + +--- + +## Metadata + +| Field | Value | +| ------------ | ------------------------------------------------------------------------------------------------------------ | +| Document | Security Policy | +| Path | /SECURITY.md | +| Repository | [https://github.com/mokoconsulting-tech/Template-Joomla](https://github.com/mokoconsulting-tech/Template-Joomla) | +| Owner | Moko Consulting | +| Scope | Security vulnerability handling | +| Status | Active | +| Effective | 2026-01-16 | + +## Revision History + +| Date | Change Description | Author | +| ---------- | ------------------------------------------------- | --------------- | +| 2026-01-16 | Initial creation for template repository | Moko Consulting | From 94201082d204d50a91bb22dccbb8d545443e3743 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sun, 28 Jun 2026 07:46:41 +0000 Subject: [PATCH 26/41] chore: sync ci-issue-reporter.yml from Template-Joomla Authored-by: Moko Consulting --- .mokogitea/workflows/ci-issue-reporter.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .mokogitea/workflows/ci-issue-reporter.yml diff --git a/.mokogitea/workflows/ci-issue-reporter.yml b/.mokogitea/workflows/ci-issue-reporter.yml new file mode 100644 index 0000000..b396c34 --- /dev/null +++ b/.mokogitea/workflows/ci-issue-reporter.yml @@ -0,0 +1 @@ +IyBDb3B5cmlnaHQgKEMpIDIwMjYgTW9rbyBDb25zdWx0aW5nIDxoZWxsb0Btb2tvY29uc3VsdGluZy50ZWNoPgojCiMgU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEdQTC0zLjAtb3ItbGF0ZXIKIwojIEZJTEUgSU5GT1JNQVRJT04KIyBERUZHUk9VUDogR2l0ZWEuV29ya2Zsb3cKIyBJTkdST1VQOiBtb2tvY2xpLlVuaXZlcnNhbAojIFJFUE86IGh0dHBzOi8vZ2l0Lm1va29jb25zdWx0aW5nLnRlY2gvTW9rb0NvbnN1bHRpbmcvbW9rb2NsaQojIFBBVEg6IC8ubW9rb2dpdGVhL3dvcmtmbG93cy9jaS1pc3N1ZS1yZXBvcnRlci55bWwKIyBWRVJTSU9OOiAwMS4wMC4wMAojIEJSSUVGOiBSZXVzYWJsZSB3b3JrZmxvdyDigJQgY3JlYXRlcy91cGRhdGVzIGEgR2l0ZWEgaXNzdWUgd2hlbiBhIENJIGdhdGUgZmFpbHMuCiMgICAgICAgIENsb25lcyBNb2tvQ0xJIGFuZCBydW5zIGNsaS9jaV9pc3N1ZV9yZXBvcnRlci5zaC4KCm5hbWU6ICJVbml2ZXJzYWw6IENJIElzc3VlIFJlcG9ydGVyIgoKb246CiAgd29ya2Zsb3dfY2FsbDoKICAgIGlucHV0czoKICAgICAgZ2F0ZToKICAgICAgICBkZXNjcmlwdGlvbjogIkNJIGdhdGUgbmFtZSAoZS5nLiBQUiBWYWxpZGF0aW9uLCBSZXBvc2l0b3J5IEhlYWx0aCkiCiAgICAgICAgcmVxdWlyZWQ6IHRydWUKICAgICAgICB0eXBlOiBzdHJpbmcKICAgICAgZGV0YWlsczoKICAgICAgICBkZXNjcmlwdGlvbjogIkh1bWFuLXJlYWRhYmxlIGZhaWx1cmUgZGVzY3JpcHRpb24iCiAgICAgICAgcmVxdWlyZWQ6IHRydWUKICAgICAgICB0eXBlOiBzdHJpbmcKICAgICAgc2V2ZXJpdHk6CiAgICAgICAgZGVzY3JpcHRpb246ICJlcnJvciBvciB3YXJuaW5nIgogICAgICAgIHJlcXVpcmVkOiBmYWxzZQogICAgICAgIHR5cGU6IHN0cmluZwogICAgICAgIGRlZmF1bHQ6ICJlcnJvciIKICAgICAgd29ya2Zsb3c6CiAgICAgICAgZGVzY3JpcHRpb246ICJXb3JrZmxvdyBuYW1lIGZvciB0aGUgaXNzdWUgdGl0bGUiCiAgICAgICAgcmVxdWlyZWQ6IGZhbHNlCiAgICAgICAgdHlwZTogc3RyaW5nCiAgICAgICAgZGVmYXVsdDogIiIKICAgIHNlY3JldHM6CiAgICAgIE1PS09HSVRFQV9UT0tFTjoKICAgICAgICByZXF1aXJlZDogdHJ1ZQoKZW52OgogIEZPUkNFX0pBVkFTQ1JJUFRfQUNUSU9OU19UT19OT0RFMjQ6IHRydWUKCmpvYnM6CiAgcmVwb3J0OgogICAgbmFtZTogIlJlcG9ydDogJHt7IGlucHV0cy5nYXRlIH19IgogICAgcnVucy1vbjogdWJ1bnR1LWxhdGVzdAoKICAgIHN0ZXBzOgogICAgICAtIG5hbWU6IENsb25lIE1va29DTEkKICAgICAgICBlbnY6CiAgICAgICAgICBNT0tPR0lURUFfVE9LRU46ICR7eyBzZWNyZXRzLk1PS09HSVRFQV9UT0tFTiB9fQogICAgICAgIHJ1bjogfAogICAgICAgICAgTU9LT0dJVEVBX1VSTD0iJHt7IHZhcnMuR0lURUFfVVJMIHx8ICdodHRwczovL2dpdC5tb2tvY29uc3VsdGluZy50ZWNoJyB9fSIKICAgICAgICAgIGdpdCBjbG9uZSAtLWRlcHRoIDEgLS1maWx0ZXI9YmxvYjpub25lIC0tc3BhcnNlICIke01PS09HSVRFQV9VUkx9L01va29Db25zdWx0aW5nL01va29DTEkuZ2l0IiAvdG1wL21va29jbGkKICAgICAgICAgIGNkIC90bXAvbW9rb2NsaSAmJiBnaXQgc3BhcnNlLWNoZWNrb3V0IHNldCBjbGkvY2lfaXNzdWVfcmVwb3J0ZXIuc2gKCiAgICAgIC0gbmFtZTogUmVwb3J0IENJIGZhaWx1cmUKICAgICAgICBlbnY6CiAgICAgICAgICBNT0tPR0lURUFfVE9LRU46ICR7eyBzZWNyZXRzLk1PS09HSVRFQV9UT0tFTiB9fQogICAgICAgICAgTU9LT0dJVEVBX1VSTDogJHt7IHZhcnMuR0lURUFfVVJMIHx8ICdodHRwczovL2dpdC5tb2tvY29uc3VsdGluZy50ZWNoJyB9fQogICAgICAgIHJ1bjogfAogICAgICAgICAgY2htb2QgK3ggL3RtcC9tb2tvY2xpL2NsaS9jaV9pc3N1ZV9yZXBvcnRlci5zaAogICAgICAgICAgL3RtcC9tb2tvY2xpL2NsaS9jaV9pc3N1ZV9yZXBvcnRlci5zaCBcCiAgICAgICAgICAgIC0tZ2F0ZSAiJHt7IGlucHV0cy5nYXRlIH19IiBcCiAgICAgICAgICAgIC0tZGV0YWlscyAiJHt7IGlucHV0cy5kZXRhaWxzIH19IiBcCiAgICAgICAgICAgIC0tc2V2ZXJpdHkgIiR7eyBpbnB1dHMuc2V2ZXJpdHkgfX0iIFwKICAgICAgICAgICAgLS13b3JrZmxvdyAiJHt7IGlucHV0cy53b3JrZmxvdyB9fSIK \ No newline at end of file From 9991bb309926ea4245fba79237f9491bdb155af3 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sun, 28 Jun 2026 07:52:37 +0000 Subject: [PATCH 27/41] chore: sync version-set.yml from Template-Joomla Authored-by: Moko Consulting --- .mokogitea/workflows/version-set.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .mokogitea/workflows/version-set.yml diff --git a/.mokogitea/workflows/version-set.yml b/.mokogitea/workflows/version-set.yml new file mode 100644 index 0000000..20afb6d --- /dev/null +++ b/.mokogitea/workflows/version-set.yml @@ -0,0 +1 @@ +IyBDb3B5cmlnaHQgKEMpIDIwMjYgTW9rbyBDb25zdWx0aW5nIDxoZWxsb0Btb2tvY29uc3VsdGluZy50ZWNoPgojCiMgU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEdQTC0zLjAtb3ItbGF0ZXIKIwojIEZJTEUgSU5GT1JNQVRJT04KIyBERUZHUk9VUDogR2l0ZWEuV29ya2Zsb3cuVGVtcGxhdGUKIyBJTkdST1VQOiBNb2tvU3RhbmRhcmRzLkNJCiMgUkVQTzogaHR0cHM6Ly9naXQubW9rb2NvbnN1bHRpbmcudGVjaC9Nb2tvQ29uc3VsdGluZy9UZW1wbGF0ZS1Kb29tbGEKIyBQQVRIOiAvLm1va29naXRlYS93b3JrZmxvd3MvdmVyc2lvbi1zZXQueW1sCiMgVkVSU0lPTjogMDEuMDAuMDAKIyBCUklFRjogU2V0IG9yIHJlc2V0IHRoZSBleHRlbnNpb24gdmVyc2lvbiBhY3Jvc3MgYWxsIHZlcnNpb24tYmVhcmluZyBmaWxlcwoKbmFtZTogIkpvb21sYTogU2V0IFZlcnNpb24iCgpvbjoKICB3b3JrZmxvd19kaXNwYXRjaDoKICAgIGlucHV0czoKICAgICAgdmVyc2lvbjoKICAgICAgICBkZXNjcmlwdGlvbjogIlZlcnNpb24gbnVtYmVyIChlLmcuIDAxLjAwLjAwKSIKICAgICAgICByZXF1aXJlZDogdHJ1ZQogICAgICAgIHR5cGU6IHN0cmluZwogICAgICBicmFuY2g6CiAgICAgICAgZGVzY3JpcHRpb246ICJCcmFuY2ggdG8gdXBkYXRlIChkZWZhdWx0OiBjdXJyZW50KSIKICAgICAgICByZXF1aXJlZDogZmFsc2UKICAgICAgICB0eXBlOiBzdHJpbmcKCnBlcm1pc3Npb25zOgogIGNvbnRlbnRzOiB3cml0ZQoKZW52OgogIEZPUkNFX0pBVkFTQ1JJUFRfQUNUSU9OU19UT19OT0RFMjQ6IHRydWUKCmpvYnM6CiAgc2V0LXZlcnNpb246CiAgICBuYW1lOiBTZXQgVmVyc2lvbiB0byAke3sgaW5wdXRzLnZlcnNpb24gfX0KICAgIHJ1bnMtb246IHVidW50dS1sYXRlc3QKCiAgICBzdGVwczoKICAgICAgLSBuYW1lOiBWYWxpZGF0ZSB2ZXJzaW9uIGZvcm1hdAogICAgICAgIHJ1bjogfAogICAgICAgICAgVkVSU0lPTj0iJHt7IGlucHV0cy52ZXJzaW9uIH19IgogICAgICAgICAgaWYgISBlY2hvICIkVkVSU0lPTiIgfCBncmVwIC1xUCAnXlxkezJ9XC5cZHsyfVwuXGR7Mn0kJzsgdGhlbgogICAgICAgICAgICBlY2hvICI6OmVycm9yOjpJbnZhbGlkIHZlcnNpb24gZm9ybWF0ICcke1ZFUlNJT059JyDigJQgZXhwZWN0ZWQgWFguWVkuWlogKGUuZy4gMDEuMDAuMDApIgogICAgICAgICAgICBleGl0IDEKICAgICAgICAgIGZpCiAgICAgICAgICBlY2hvICJWRVJTSU9OPSR7VkVSU0lPTn0iID4+ICIkR0lUSFVCX0VOViIKCiAgICAgIC0gbmFtZTogQ2hlY2tvdXQKICAgICAgICB1c2VzOiBhY3Rpb25zL2NoZWNrb3V0QHY0CiAgICAgICAgd2l0aDoKICAgICAgICAgIHRva2VuOiAke3sgc2VjcmV0cy5NT0tPR0lURUFfVE9LRU4gfHwgZ2l0aHViLnRva2VuIH19CiAgICAgICAgICByZWY6ICR7eyBpbnB1dHMuYnJhbmNoIHx8IGdpdGh1Yi5yZWYgfX0KICAgICAgICAgIGZldGNoLWRlcHRoOiAxCgogICAgICAtIG5hbWU6IFVwZGF0ZSBtYW5pZmVzdCB2ZXJzaW9uCiAgICAgICAgcnVuOiB8CiAgICAgICAgICBNQU5JRkVTVD0iIgogICAgICAgICAgZm9yIFhNTF9GSUxFIGluICQoZmluZCAuIC1tYXhkZXB0aCAzIC1uYW1lICIqLnhtbCIgLW5vdCAtcGF0aCAiLi8uZ2l0LyoiIC1ub3QgLXBhdGggIi4vdmVuZG9yLyoiKTsgZG8KICAgICAgICAgICAgaWYgZ3JlcCAtcSAiPGV4dGVuc2lvbiIgIiRYTUxfRklMRSIgMj4vZGV2L251bGw7IHRoZW4KICAgICAgICAgICAgICBNQU5JRkVTVD0iJFhNTF9GSUxFIgogICAgICAgICAgICAgIGJyZWFrCiAgICAgICAgICAgIGZpCiAgICAgICAgICBkb25lCgogICAgICAgICAgaWYgWyAteiAiJE1BTklGRVNUIiBdOyB0aGVuCiAgICAgICAgICAgIGVjaG8gIjo6d2FybmluZzo6Tm8gSm9vbWxhIGV4dGVuc2lvbiBtYW5pZmVzdCBmb3VuZCDigJQgc2tpcHBpbmcgbWFuaWZlc3QgdXBkYXRlIgogICAgICAgICAgZWxzZQogICAgICAgICAgICBPTERfVkVSPSQoZ3JlcCAtb1AgJzx2ZXJzaW9uPlxLW148XSsnICIkTUFOSUZFU1QiIHwgaGVhZCAtMSkKICAgICAgICAgICAgc2VkIC1pICJzfDx2ZXJzaW9uPiR7T0xEX1ZFUn08L3ZlcnNpb24+fDx2ZXJzaW9uPiR7VkVSU0lPTn08L3ZlcnNpb24+fCIgIiRNQU5JRkVTVCIKICAgICAgICAgICAgZWNobyAiTWFuaWZlc3Q6ICR7T0xEX1ZFUn0g4oaSICR7VkVSU0lPTn0gKCR7TUFOSUZFU1R9KSIKICAgICAgICAgIGZpCgogICAgICAtIG5hbWU6IFVwZGF0ZSBSRUFETUUubWQgdmVyc2lvbgogICAgICAgIHJ1bjogfAogICAgICAgICAgaWYgWyAtZiAiUkVBRE1FLm1kIiBdOyB0aGVuCiAgICAgICAgICAgIGlmIGdyZXAgLXFQICdeXHMqVkVSU0lPTjpccypcZCcgUkVBRE1FLm1kOyB0aGVuCiAgICAgICAgICAgICAgc2VkIC1pIC1FICJzLyhWRVJTSU9OOlxzKilbMC05XXsyfVwuWzAtOV17Mn1cLlswLTldezJ9L1wxJHtWRVJTSU9OfS8iIFJFQURNRS5tZAogICAgICAgICAgICAgIGVjaG8gIlJFQURNRS5tZCB2ZXJzaW9uIHVwZGF0ZWQgdG8gJHtWRVJTSU9OfSIKICAgICAgICAgICAgZWxzZQogICAgICAgICAgICAgIGVjaG8gIjo6d2FybmluZzo6Tm8gVkVSU0lPTiBsaW5lIGZvdW5kIGluIFJFQURNRS5tZCDigJQgc2tpcHBpbmciCiAgICAgICAgICAgIGZpCiAgICAgICAgICBmaQoKICAgICAgLSBuYW1lOiBVcGRhdGUgQ0hBTkdFTE9HLm1kCiAgICAgICAgcnVuOiB8CiAgICAgICAgICBpZiBbIC1mICJDSEFOR0VMT0cubWQiIF07IHRoZW4KICAgICAgICAgICAgREFURT0kKGRhdGUgKyVZLSVtLSVkKQogICAgICAgICAgICAjIENoZWNrIGlmIHRoaXMgdmVyc2lvbiBhbHJlYWR5IGhhcyBhbiBlbnRyeQogICAgICAgICAgICBpZiBncmVwIC1xICJeXCNcIyBcWyR7VkVSU0lPTn1cXSIgQ0hBTkdFTE9HLm1kOyB0aGVuCiAgICAgICAgICAgICAgZWNobyAiQ0hBTkdFTE9HLm1kIGFscmVhZHkgaGFzIGVudHJ5IGZvciAke1ZFUlNJT059IOKAlCBza2lwcGluZyIKICAgICAgICAgICAgZWxzZQogICAgICAgICAgICAgICMgSW5zZXJ0IG5ldyB2ZXJzaW9uIGVudHJ5IGFmdGVyIFtVbnJlbGVhc2VkXSBvciBhdCB0aGUgdG9wIGFmdGVyIGhlYWRlcgogICAgICAgICAgICAgIGlmIGdyZXAgLXEgJ15cI1wjIFxbVW5yZWxlYXNlZFxdJyBDSEFOR0VMT0cubWQ7IHRoZW4KICAgICAgICAgICAgICAgIHNlZCAtaSAiL15cI1wjIFxbVW5yZWxlYXNlZFxdL2FcXFxcbiMjIFske1ZFUlNJT059XSAtLS0gJHtEQVRFfSIgQ0hBTkdFTE9HLm1kCiAgICAgICAgICAgICAgZWxzZQogICAgICAgICAgICAgICAgc2VkIC1pICIvXlwjIENoYW5nZWxvZy9hXFxcXG4jIyBbVW5yZWxlYXNlZF1cblxuIyMgWyR7VkVSU0lPTn1dIC0tLSAke0RBVEV9IiBDSEFOR0VMT0cubWQKICAgICAgICAgICAgICBmaQogICAgICAgICAgICAgIGVjaG8gIkNIQU5HRUxPRy5tZDogYWRkZWQgZW50cnkgZm9yICR7VkVSU0lPTn0iCiAgICAgICAgICAgIGZpCiAgICAgICAgICBlbHNlCiAgICAgICAgICAgIGVjaG8gIjo6d2FybmluZzo6Tm8gQ0hBTkdFTE9HLm1kIGZvdW5kIOKAlCBza2lwcGluZyIKICAgICAgICAgIGZpCgogICAgICAtIG5hbWU6IFVwZGF0ZSBGSUxFIElORk9STUFUSU9OIGJsb2NrcwogICAgICAgIHJ1bjogfAogICAgICAgICAgIyBVcGRhdGUgVkVSU0lPTiBpbiBmaWxlIGhlYWRlciBibG9ja3MgKCMgVkVSU0lPTjogWFguWVkuWlopCiAgICAgICAgICBmaW5kIC4gLW1heGRlcHRoIDEgLXR5cGUgZiBcKCAtbmFtZSAiKi55bWwiIC1vIC1uYW1lICIqLnlhbWwiIC1vIC1uYW1lICIqLnBocCIgLW8gLW5hbWUgIioubWQiIFwpIFwKICAgICAgICAgICAgLW5vdCAtcGF0aCAiLi8uZ2l0LyoiIC1ub3QgLXBhdGggIi4vdmVuZG9yLyoiIC1wcmludDAgMj4vZGV2L251bGwgfCBcCiAgICAgICAgICB3aGlsZSBJRlM9IHJlYWQgLXIgLWQgJycgRklMRTsgZG8KICAgICAgICAgICAgaWYgaGVhZCAtMjAgIiRGSUxFIiB8IGdyZXAgLXFQICdeXHMqIz9ccypWRVJTSU9OOlxzKlxkezJ9XC5cZHsyfVwuXGR7Mn0nOyB0aGVuCiAgICAgICAgICAgICAgc2VkIC1pIC1FICJzLygjP1xzKlZFUlNJT046XHMqKVswLTldezJ9XC5bMC05XXsyfVwuWzAtOV17Mn0vXDEke1ZFUlNJT059LyIgIiRGSUxFIgogICAgICAgICAgICAgIGVjaG8gIlVwZGF0ZWQgRklMRSBJTkZPUk1BVElPTiBWRVJTSU9OIGluICR7RklMRX0iCiAgICAgICAgICAgIGZpCiAgICAgICAgICBkb25lCgogICAgICAtIG5hbWU6IENvbW1pdCBhbmQgcHVzaAogICAgICAgIHJ1bjogfAogICAgICAgICAgZ2l0IGNvbmZpZyB1c2VyLm5hbWUgIk1va28gQ29uc3VsdGluZyBbYm90XSIKICAgICAgICAgIGdpdCBjb25maWcgdXNlci5lbWFpbCAiaGVsbG9AbW9rb2NvbnN1bHRpbmcudGVjaCIKICAgICAgICAgIGdpdCBhZGQgLUEKICAgICAgICAgIGlmIGdpdCBkaWZmIC0tY2FjaGVkIC0tcXVpZXQ7IHRoZW4KICAgICAgICAgICAgZWNobyAiTm8gdmVyc2lvbiBjaGFuZ2VzIGRldGVjdGVkIOKAlCBub3RoaW5nIHRvIGNvbW1pdCIKICAgICAgICAgIGVsc2UKICAgICAgICAgICAgZ2l0IGNvbW1pdCAtbSAiY2hvcmU6IHNldCB2ZXJzaW9uIHRvICR7VkVSU0lPTn0gW3NraXAgYnVtcF0KCkF1dGhvcmVkLWJ5OiBNb2tvIENvbnN1bHRpbmciCiAgICAgICAgICAgIGdpdCBwdXNoCiAgICAgICAgICAgIGVjaG8gIiMjIyBWZXJzaW9uIFNldCIgPj4gJEdJVEhVQl9TVEVQX1NVTU1BUlkKICAgICAgICAgICAgZWNobyAiVmVyc2lvbiB1cGRhdGVkIHRvIFxgJHtWRVJTSU9OfVxgIG9uIGJyYW5jaCBcYCR7R0lUSFVCX1JFRl9OQU1FfVxgIiA+PiAkR0lUSFVCX1NURVBfU1VNTUFSWQogICAgICAgICAgZmkK \ No newline at end of file From 31da0a5980b831074aed26aabe7b064d1ce17105 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sun, 28 Jun 2026 07:54:35 +0000 Subject: [PATCH 28/41] chore: sync GOVERNANCE.md from Template-Joomla Authored-by: Moko Consulting --- GOVERNANCE.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 GOVERNANCE.md diff --git a/GOVERNANCE.md b/GOVERNANCE.md new file mode 100644 index 0000000..bad8ed0 --- /dev/null +++ b/GOVERNANCE.md @@ -0,0 +1 @@ +PCEtLQogQ29weXJpZ2h0IChDKSAyMDI2IE1va28gQ29uc3VsdGluZyA8aGVsbG9AbW9rb2NvbnN1bHRpbmcudGVjaD4KCiBUaGlzIGZpbGUgaXMgcGFydCBvZiBhIE1va28gQ29uc3VsdGluZyBwcm9qZWN0LgoKIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBHUEwtMy4wLW9yLWxhdGVyCgogVGhpcyBwcm9ncmFtIGlzIGZyZWUgc29mdHdhcmU7IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mCiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5IHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb247IGVpdGhlciB2ZXJzaW9uIDMKIG9mIHRoZSBMaWNlbnNlLCBvciAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLgoKIFRoaXMgcHJvZ3JhbSBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLCBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7CiB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLgogU2VlIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgoKIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlICguL0xJQ0VOU0UpLgoKIEZJTEUgSU5GT1JNQVRJT04KIERFRkdST1VQOiBtb2tvY29uc3VsdGluZy10ZWNoLlRlbXBsYXRlLUpvb21sYQogSU5HUk9VUDogTW9rb1N0YW5kYXJkcy5Hb3Zlcm5hbmNlCiBSRVBPOiBodHRwczovL2dpdGh1Yi5jb20vbW9rb2NvbnN1bHRpbmctdGVjaC9UZW1wbGF0ZS1Kb29tbGEKIFZFUlNJT046IDAxLjAxLjAwCiBQQVRIOiAvR09WRVJOQU5DRS5tZAogQlJJRUY6IFByb2plY3QgZ292ZXJuYW5jZSBydWxlcywgcm9sZXMsIGFuZCBkZWNpc2lvbiBwcm9jZXNzIGZvciBUZW1wbGF0ZS1Kb29tbGEKLS0+CgpbIVtNb2tvU3RhbmRhcmRzXShodHRwczovL2ltZy5zaGllbGRzLmlvL2JhZGdlL01va29TdGFuZGFyZHMtMDQuMDAuMDQtYmx1ZSldKGh0dHBzOi8vZ2l0aHViLmNvbS9tb2tvY29uc3VsdGluZy10ZWNoL01va29TdGFuZGFyZHMpCgojIFByb2plY3QgR292ZXJuYW5jZQoKIyMgT3ZlcnZpZXcKClRoaXMgZG9jdW1lbnQgZGVmaW5lcyB0aGUgZ292ZXJuYW5jZSBtb2RlbCBmb3IgdGhlIGBUZW1wbGF0ZS1Kb29tbGFgIHJlcG9zaXRvcnkgd2l0aGluIHRoZQpgbW9rb2NvbnN1bHRpbmctdGVjaGAgb3JnYW5pemF0aW9uLiBJdCBpcyBhdXRvbWF0aWNhbGx5IG1haW50YWluZWQgYnkKW01va29TdGFuZGFyZHNdKGh0dHBzOi8vZ2l0aHViLmNvbS9tb2tvY29uc3VsdGluZy10ZWNoL01va29TdGFuZGFyZHMpIHYwNC4wMC4wNC4KCkZ1bGwgZ292ZXJuYW5jZSBwb2xpY3kgaXMgZGVmaW5lZCBpbiB0aGUgTW9rb1N0YW5kYXJkcyBzb3VyY2UgcmVwb3NpdG9yeToKW2RvY3MvcG9saWN5L0dPVkVSTkFOQ0UubWRdKGh0dHBzOi8vZ2l0aHViLmNvbS9tb2tvY29uc3VsdGluZy10ZWNoL01va29TdGFuZGFyZHMvYmxvYi9tYWluL2RvY3MvcG9saWN5L0dPVkVSTkFOQ0UubWQpCgotLS0KCiMjIFJvbGVzIGFuZCBSZXNwb25zaWJpbGl0aWVzCgojIyMgTWFpbnRhaW5lcgoKKipHaXRIdWIqKjogQG1va29jb25zdWx0aW5nLXRlY2gKCioqQXV0aG9yaXR5Kio6IEZpbmFsIGRlY2lzaW9uLW1ha2luZyBhdXRob3JpdHkgb24gYWxsIG1hdHRlcnMgZm9yIHRoaXMgcmVwb3NpdG9yeS4KCioqUmVzcG9uc2liaWxpdGllcyoqOgotIFJldmlldyBhbmQgbWVyZ2UgcHVsbCByZXF1ZXN0cwotIE1haW50YWluIGNvZGUgcXVhbGl0eSBhbmQgc3RhbmRhcmRzIGNvbXBsaWFuY2UKLSBNYW5hZ2UgcmVsZWFzZXMgYW5kIHZlcnNpb25pbmcKLSBSZXNwb25kIHRvIGlzc3VlcyBhbmQgc2VjdXJpdHkgcmVwb3J0cwoKIyMjIENvbnRyaWJ1dG9ycwoKKipBdXRob3JpdHkqKjogU3VibWl0IGNoYW5nZXMgdmlhIHB1bGwgcmVxdWVzdHMuCgoqKlJlcXVpcmVtZW50cyoqOgotIFJlYWQgYW5kIGFjY2VwdCBgQ09ERV9PRl9DT05EVUNULm1kYAotIEZvbGxvdyBgQ09OVFJJQlVUSU5HLm1kYCBndWlkZWxpbmVzCgotLS0KCiMjIERlY2lzaW9uLU1ha2luZwoKQWxsIGNoYW5nZXMgbXVzdCBiZSBzdWJtaXR0ZWQgYXMgcHVsbCByZXF1ZXN0cy4gVGhlIG1haW50YWluZXIgKEBtb2tvY29uc3VsdGluZy10ZWNoKQpyZXZpZXdzIGFuZCBhcHByb3ZlcyBhbGwgY2hhbmdlcyBiZWZvcmUgdGhleSBhcmUgbWVyZ2VkLgoKIyMjIFNvbGUgT3BlcmF0b3IgUG9saWN5CgpUaGlzIG9yZ2FuaXphdGlvbiBvcGVyYXRlcyB1bmRlciBhICoqc29sZSBvcGVyYXRvcioqIG1vZGVsLiBUaGUgbWFpbnRhaW5lciAoQG1va29jb25zdWx0aW5nLXRlY2gpCmlzIHRoZSBzb2xlIGVtcGxveWVlIGFuZCBvd25lciBhbmQgbWF5IHNlbGYtYXBwcm92ZSBwdWxsIHJlcXVlc3RzIHdoZW4gbm8gc2Vjb25kIHJldmlld2VyIGlzCmF2YWlsYWJsZS4gVGhlIGZvbGxvd2luZyByZXF1aXJlbWVudHMgcmVtYWluIG1hbmRhdG9yeSByZWdhcmRsZXNzOgoKMS4gKipQdWxsIFJlcXVlc3RzIFJlcXVpcmVkKiog4oCUIGFsbCBjaGFuZ2VzIHRvIHByb3RlY3RlZCBicmFuY2hlcyBnbyB0aHJvdWdoIGEgUFIuCjIuICoqQXV0b21hdGVkIENoZWNrcyoqIOKAlCBhbGwgQ0kgY2hlY2tzIG11c3QgcGFzcyBiZWZvcmUgbWVyZ2luZy4KMy4gKipBdWRpdCBUcmFpbCoqIOKAlCBpc3N1ZXMsIHB1bGwgcmVxdWVzdHMsIGFuZCBjb21taXQgaGlzdG9yeSBhcmUgcHJlc2VydmVkLgo0LiAqKkRvY3VtZW50YXRpb24qKiDigJQgY2hhbmdlcyBhcmUgZG9jdW1lbnRlZCBpbiBgQ0hBTkdFTE9HLm1kYC4KClNlZSB0aGUgZnVsbCBwb2xpY3k6CltTb2xlIE9wZXJhdG9yIFBvbGljeV0oaHR0cHM6Ly9naXRodWIuY29tL21va29jb25zdWx0aW5nLXRlY2gvTW9rb1N0YW5kYXJkcy9ibG9iL21haW4vZG9jcy9wb2xpY3kvR09WRVJOQU5DRS5tZCNzb2xlLW9wZXJhdG9yLXBvbGljeSkKCi0tLQoKIyMgQ2hhbmdlIE1hbmFnZW1lbnQKCnwgQ2hhbmdlIFR5cGUgfCBBcHByb3ZhbCB8IFByb2Nlc3MgfAp8LS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tfC0tLS0tLS0tLXwKfCBSb3V0aW5lIChkb2NzLCBidWcgZml4ZXMpIHwgTWFpbnRhaW5lciB8IFBSIOKGkiBDSSBwYXNzIOKGkiBtZXJnZSB8CnwgU2lnbmlmaWNhbnQgKG5ldyBmZWF0dXJlcykgfCBNYWludGFpbmVyIHwgUFIgd2l0aCBkZXNjcmlwdGlvbiDihpIgQ0kgcGFzcyDihpIgbWVyZ2UgfAp8IE1ham9yIChicmVha2luZywgYXJjaGl0ZWN0dXJlKSB8IE1haW50YWluZXIgfCBJc3N1ZSBkaXNjdXNzaW9uIOKGkiBQUiDihpIgQ0kgcGFzcyDihpIgbWVyZ2UgfAp8IEVtZXJnZW5jeSAoc2VjdXJpdHkpIHwgTWFpbnRhaW5lciB8IExhYmVsbGVkIGBFTUVSR0VOQ1lgIOKGkiBpbW1lZGlhdGUgbWVyZ2Ug4oaSIHBvc3QtbW9ydGVtIHwKCi0tLQoKIyMgUmVwb3J0aW5nIElzc3VlcwoKLSAqKkJ1Z3MgLyBGZWF0dXJlcyoqOiBPcGVuIGEgW0dpdEh1YiBJc3N1ZV0oaHR0cHM6Ly9naXRodWIuY29tL21va29jb25zdWx0aW5nLXRlY2gvVGVtcGxhdGUtSm9vbWxhL2lzc3VlcykKLSAqKlNlY3VyaXR5IHZ1bG5lcmFiaWxpdGllcyoqOiBTZWUgW1NFQ1VSSVRZLm1kXSguL1NFQ1VSSVRZLm1kKQotICoqQ29kZSBvZiBDb25kdWN0Kio6IFNlZSBbQ09ERV9PRl9DT05EVUNULm1kXSguL0NPREVfT0ZfQ09ORFVDVC5tZCkKLSAqKkNvbnRhY3QqKjogZGV2QG1va29jb25zdWx0aW5nLnRlY2gKCi0tLQoKIyMgTWV0YWRhdGEKCnwgRmllbGQgICAgICAgICB8IFZhbHVlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCAtLS0tLS0tLS0tLS0tIHwgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gfAp8IERvY3VtZW50IFR5cGUgfCBQb2xpY3kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgRG9tYWluICAgICAgICB8IEdvdmVybmFuY2UgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBBcHBsaWVzIFRvICAgIHwgbW9rb2NvbnN1bHRpbmctdGVjaC9UZW1wbGF0ZS1Kb29tbGEgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgSnVyaXNkaWN0aW9uICB8IFRlbm5lc3NlZSwgVVNBICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBNYWludGFpbmVyICAgIHwgQG1va29jb25zdWx0aW5nLXRlY2ggICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IFN0YW5kYXJkcyAgICAgfCBNb2tvU3RhbmRhcmRzIHYwNC4wMC4wNCAgICAgICAgICAgIHwKfCBSZXBvICAgICAgICAgIHwgaHR0cHM6Ly9naXRodWIuY29tL21va29jb25zdWx0aW5nLXRlY2gvVGVtcGxhdGUtSm9vbWxhICAgICAgICB8CnwgUGF0aCAgICAgICAgICB8IC9HT1ZFUk5BTkNFLm1kICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBTdGF0dXMgICAgICAgIHwgQWN0aXZlIOKAlCBhdXRvLW1haW50YWluZWQgYnkgTW9rb1N0YW5kYXJkcyAgICAgICB8Cg== \ No newline at end of file From c65ef345ef73e4f9dfa207ac18b426f43f5491a6 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" <gitea-actions[bot]@mokoconsulting.tech> Date: Sun, 28 Jun 2026 08:05:36 +0000 Subject: [PATCH 29/41] chore(version): pre-release bump to 01.04.09-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 2 +- README.md | 2 +- SECURITY.md | 2 +- source/packages/com_mokoog/mokoog.xml | 2 +- source/packages/com_mokoog/sql/updates/mysql/01.04.09.sql | 1 + source/packages/plg_content_mokoog/mokoog.xml | 2 +- source/packages/plg_system_mokoog/mokoog.xml | 2 +- source/packages/plg_webservices_mokoog/mokoog.xml | 2 +- source/pkg_mokoog.xml | 2 +- 10 files changed, 10 insertions(+), 9 deletions(-) create mode 100644 source/packages/com_mokoog/sql/updates/mysql/01.04.09.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 194bf30..06e0575 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.04.08 +# VERSION: 01.04.09 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index 504615f..aef5c51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -<!-- VERSION: 01.04.08 --> +<!-- VERSION: 01.04.09 --> All notable changes to MokoSuiteOpenGraph will be documented in this file. diff --git a/README.md b/README.md index f4323db..0a7a8c1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoSuiteOpenGraph -<!-- VERSION: 01.04.08 --> +<!-- VERSION: 01.04.09 --> Open Graph, Twitter Card, and social sharing meta tag management for Joomla 4/5/6. diff --git a/SECURITY.md b/SECURITY.md index 81c22ff..fd4b166 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 01.01.00 +VERSION: 01.04.09 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/com_mokoog/mokoog.xml b/source/packages/com_mokoog/mokoog.xml index 4bbfc08..5638a21 100644 --- a/source/packages/com_mokoog/mokoog.xml +++ b/source/packages/com_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="component" method="upgrade"> <name>com_mokoog</name> - <version>01.04.08</version> + <version>01.04.09</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/com_mokoog/sql/updates/mysql/01.04.09.sql b/source/packages/com_mokoog/sql/updates/mysql/01.04.09.sql new file mode 100644 index 0000000..d5246e9 --- /dev/null +++ b/source/packages/com_mokoog/sql/updates/mysql/01.04.09.sql @@ -0,0 +1 @@ +/* 01.04.09 — no schema changes */ diff --git a/source/packages/plg_content_mokoog/mokoog.xml b/source/packages/plg_content_mokoog/mokoog.xml index 9cb592a..48367c7 100644 --- a/source/packages/plg_content_mokoog/mokoog.xml +++ b/source/packages/plg_content_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="content" method="upgrade"> <name>Content - MokoJoomOpenGraph</name> - <version>01.04.08</version> + <version>01.04.09</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_system_mokoog/mokoog.xml b/source/packages/plg_system_mokoog/mokoog.xml index b0f7ae3..9ba17e9 100644 --- a/source/packages/plg_system_mokoog/mokoog.xml +++ b/source/packages/plg_system_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="system" method="upgrade"> <name>System - MokoJoomOpenGraph</name> - <version>01.04.08</version> + <version>01.04.09</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_webservices_mokoog/mokoog.xml b/source/packages/plg_webservices_mokoog/mokoog.xml index 4094a1f..fb7374e 100644 --- a/source/packages/plg_webservices_mokoog/mokoog.xml +++ b/source/packages/plg_webservices_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="webservices" method="upgrade"> <name>Web Services - MokoJoomOpenGraph</name> - <version>01.04.08</version> + <version>01.04.09</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/pkg_mokoog.xml b/source/pkg_mokoog.xml index 6a73a1f..4b677d4 100644 --- a/source/pkg_mokoog.xml +++ b/source/pkg_mokoog.xml @@ -8,7 +8,7 @@ <extension type="package" method="upgrade"> <name>Package - MokoSuiteOpenGraph</name> <packagename>mokoog</packagename> - <version>01.04.08</version> + <version>01.04.09</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> From 7ef082a8de835ed4891e44156655eeef058dcbf8 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" <gitea-actions[bot]@mokoconsulting.tech> Date: Sun, 28 Jun 2026 08:07:24 +0000 Subject: [PATCH 30/41] chore(version): pre-release bump to 01.04.10-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 2 +- README.md | 2 +- SECURITY.md | 2 +- source/packages/com_mokoog/mokoog.xml | 2 +- source/packages/com_mokoog/sql/updates/mysql/01.04.10.sql | 1 + source/packages/plg_content_mokoog/mokoog.xml | 2 +- source/packages/plg_system_mokoog/mokoog.xml | 2 +- source/packages/plg_webservices_mokoog/mokoog.xml | 2 +- source/pkg_mokoog.xml | 2 +- 10 files changed, 10 insertions(+), 9 deletions(-) create mode 100644 source/packages/com_mokoog/sql/updates/mysql/01.04.10.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 06e0575..6a46b4a 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.04.09 +# VERSION: 01.04.10 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index aef5c51..95738b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -<!-- VERSION: 01.04.09 --> +<!-- VERSION: 01.04.10 --> All notable changes to MokoSuiteOpenGraph will be documented in this file. diff --git a/README.md b/README.md index 0a7a8c1..cf75612 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoSuiteOpenGraph -<!-- VERSION: 01.04.09 --> +<!-- VERSION: 01.04.10 --> Open Graph, Twitter Card, and social sharing meta tag management for Joomla 4/5/6. diff --git a/SECURITY.md b/SECURITY.md index fd4b166..e47c61f 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 01.04.09 +VERSION: 01.04.10 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/com_mokoog/mokoog.xml b/source/packages/com_mokoog/mokoog.xml index 5638a21..0d74371 100644 --- a/source/packages/com_mokoog/mokoog.xml +++ b/source/packages/com_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="component" method="upgrade"> <name>com_mokoog</name> - <version>01.04.09</version> + <version>01.04.10</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/com_mokoog/sql/updates/mysql/01.04.10.sql b/source/packages/com_mokoog/sql/updates/mysql/01.04.10.sql new file mode 100644 index 0000000..361f3a0 --- /dev/null +++ b/source/packages/com_mokoog/sql/updates/mysql/01.04.10.sql @@ -0,0 +1 @@ +/* 01.04.10 — no schema changes */ diff --git a/source/packages/plg_content_mokoog/mokoog.xml b/source/packages/plg_content_mokoog/mokoog.xml index 48367c7..a546cfa 100644 --- a/source/packages/plg_content_mokoog/mokoog.xml +++ b/source/packages/plg_content_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="content" method="upgrade"> <name>Content - MokoJoomOpenGraph</name> - <version>01.04.09</version> + <version>01.04.10</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_system_mokoog/mokoog.xml b/source/packages/plg_system_mokoog/mokoog.xml index 9ba17e9..3bcc583 100644 --- a/source/packages/plg_system_mokoog/mokoog.xml +++ b/source/packages/plg_system_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="system" method="upgrade"> <name>System - MokoJoomOpenGraph</name> - <version>01.04.09</version> + <version>01.04.10</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_webservices_mokoog/mokoog.xml b/source/packages/plg_webservices_mokoog/mokoog.xml index fb7374e..b8621bf 100644 --- a/source/packages/plg_webservices_mokoog/mokoog.xml +++ b/source/packages/plg_webservices_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="webservices" method="upgrade"> <name>Web Services - MokoJoomOpenGraph</name> - <version>01.04.09</version> + <version>01.04.10</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/pkg_mokoog.xml b/source/pkg_mokoog.xml index 4b677d4..79ee05b 100644 --- a/source/pkg_mokoog.xml +++ b/source/pkg_mokoog.xml @@ -8,7 +8,7 @@ <extension type="package" method="upgrade"> <name>Package - MokoSuiteOpenGraph</name> <packagename>mokoog</packagename> - <version>01.04.09</version> + <version>01.04.10</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> From 5afbc75f2332b657ca09e80cc2e7aa9aefebef51 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" <gitea-actions[bot]@mokoconsulting.tech> Date: Sun, 28 Jun 2026 08:07:44 +0000 Subject: [PATCH 31/41] chore(version): pre-release bump to 01.04.11-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 2 +- README.md | 2 +- SECURITY.md | 2 +- source/packages/com_mokoog/mokoog.xml | 2 +- source/packages/com_mokoog/sql/updates/mysql/01.04.11.sql | 1 + source/packages/plg_content_mokoog/mokoog.xml | 2 +- source/packages/plg_system_mokoog/mokoog.xml | 2 +- source/packages/plg_webservices_mokoog/mokoog.xml | 2 +- source/pkg_mokoog.xml | 2 +- 10 files changed, 10 insertions(+), 9 deletions(-) create mode 100644 source/packages/com_mokoog/sql/updates/mysql/01.04.11.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 6a46b4a..04b599a 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.04.10 +# VERSION: 01.04.11 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index 95738b9..452ad70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -<!-- VERSION: 01.04.10 --> +<!-- VERSION: 01.04.11 --> All notable changes to MokoSuiteOpenGraph will be documented in this file. diff --git a/README.md b/README.md index cf75612..8d2b06e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoSuiteOpenGraph -<!-- VERSION: 01.04.10 --> +<!-- VERSION: 01.04.11 --> Open Graph, Twitter Card, and social sharing meta tag management for Joomla 4/5/6. diff --git a/SECURITY.md b/SECURITY.md index e47c61f..ec2a6d3 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 01.04.10 +VERSION: 01.04.11 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/com_mokoog/mokoog.xml b/source/packages/com_mokoog/mokoog.xml index 0d74371..7d52a45 100644 --- a/source/packages/com_mokoog/mokoog.xml +++ b/source/packages/com_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="component" method="upgrade"> <name>com_mokoog</name> - <version>01.04.10</version> + <version>01.04.11</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/com_mokoog/sql/updates/mysql/01.04.11.sql b/source/packages/com_mokoog/sql/updates/mysql/01.04.11.sql new file mode 100644 index 0000000..1d05f29 --- /dev/null +++ b/source/packages/com_mokoog/sql/updates/mysql/01.04.11.sql @@ -0,0 +1 @@ +/* 01.04.11 — no schema changes */ diff --git a/source/packages/plg_content_mokoog/mokoog.xml b/source/packages/plg_content_mokoog/mokoog.xml index a546cfa..5f75895 100644 --- a/source/packages/plg_content_mokoog/mokoog.xml +++ b/source/packages/plg_content_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="content" method="upgrade"> <name>Content - MokoJoomOpenGraph</name> - <version>01.04.10</version> + <version>01.04.11</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_system_mokoog/mokoog.xml b/source/packages/plg_system_mokoog/mokoog.xml index 3bcc583..70dcb41 100644 --- a/source/packages/plg_system_mokoog/mokoog.xml +++ b/source/packages/plg_system_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="system" method="upgrade"> <name>System - MokoJoomOpenGraph</name> - <version>01.04.10</version> + <version>01.04.11</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_webservices_mokoog/mokoog.xml b/source/packages/plg_webservices_mokoog/mokoog.xml index b8621bf..b1acbcf 100644 --- a/source/packages/plg_webservices_mokoog/mokoog.xml +++ b/source/packages/plg_webservices_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="webservices" method="upgrade"> <name>Web Services - MokoJoomOpenGraph</name> - <version>01.04.10</version> + <version>01.04.11</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/pkg_mokoog.xml b/source/pkg_mokoog.xml index 79ee05b..22a49f8 100644 --- a/source/pkg_mokoog.xml +++ b/source/pkg_mokoog.xml @@ -8,7 +8,7 @@ <extension type="package" method="upgrade"> <name>Package - MokoSuiteOpenGraph</name> <packagename>mokoog</packagename> - <version>01.04.10</version> + <version>01.04.11</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> From 3fb5a87be91a66d7495bf8dfbae5eee149ac3466 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <jmiller-moko@noreply.git.mokoconsulting.tech> Date: Wed, 24 Jun 2026 07:31:12 -0500 Subject: [PATCH 32/41] fix: use mysqli driver in component manifest for Joomla 4/5/6 The install/uninstall/update SQL sections used driver="mysql" which doesn't match the active mysqli driver, causing "SQL File not found" on fresh installs and silently skipping schema updates on upgrades. --- CHANGELOG.md | 1 + source/packages/com_mokoog/mokoog.xml | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 452ad70..1c1acf2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Database table `#__mokoog_tags` with multilingual unique key ### Fixed +- Fix SQL driver attribute `mysql` → `mysqli` in component manifest preventing fresh installs - Add exception logging to BatchController batch skip (#76) - Align form maxlength attributes with DB schema limits (#77) - Add `strip_tags()` input sanitization on OG text fields (#79) diff --git a/source/packages/com_mokoog/mokoog.xml b/source/packages/com_mokoog/mokoog.xml index 7d52a45..1bd2511 100644 --- a/source/packages/com_mokoog/mokoog.xml +++ b/source/packages/com_mokoog/mokoog.xml @@ -23,17 +23,17 @@ <install> <sql> - <file driver="mysql" charset="utf8">sql/install.mysql.sql</file> + <file driver="mysqli" charset="utf8">sql/install.mysql.sql</file> </sql> </install> <uninstall> <sql> - <file driver="mysql" charset="utf8">sql/uninstall.mysql.sql</file> + <file driver="mysqli" charset="utf8">sql/uninstall.mysql.sql</file> </sql> </uninstall> <update> <schemas> - <schemapath type="mysql">sql/updates/mysql</schemapath> + <schemapath type="mysqli">sql/updates/mysql</schemapath> </schemas> </update> From b77054b76999f1668ded5874893a2bccd068ef09 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <jmiller@noreply.git.mokoconsulting.tech> Date: Sun, 28 Jun 2026 13:54:53 -0500 Subject: [PATCH 33/41] fix: harden input handling and output safety (#79) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - canonical_url: sanitize via sanitizeUrl() (scheme allowlist) instead of bare trim() — closes stored-XSS via addHeadLink() on the public frontend - AI endpoint: replace die('Invalid Token') with a clean event result, and strip_tags + truncate article_title to 200 chars before use - SitemapBuilder: whitelist changefreq against the sitemap spec enum, intval() noindex IDs, strict in_array comparison - MokoOG: log a WARNING when sitemap.xml write fails instead of ignoring it --- .../src/Extension/MokoOGContent.php | 2 +- .../plg_system_mokoog/src/Extension/MokoOG.php | 12 +++++++++--- .../plg_system_mokoog/src/Helper/SitemapBuilder.php | 7 +++++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/source/packages/plg_content_mokoog/src/Extension/MokoOGContent.php b/source/packages/plg_content_mokoog/src/Extension/MokoOGContent.php index dd7d709..6f55213 100644 --- a/source/packages/plg_content_mokoog/src/Extension/MokoOGContent.php +++ b/source/packages/plg_content_mokoog/src/Extension/MokoOGContent.php @@ -274,7 +274,7 @@ final class MokoOGContent extends CMSPlugin implements SubscriberInterface 'seo_title' => strip_tags(trim($ogData['seo_title'] ?? '')), 'meta_description' => strip_tags(trim($ogData['meta_description'] ?? '')), 'robots' => trim($robots), - 'canonical_url' => trim($ogData['canonical_url'] ?? ''), + 'canonical_url' => $this->sanitizeUrl($ogData['canonical_url'] ?? ''), 'published' => 1, 'modified' => Factory::getDate()->toSql(), ]; diff --git a/source/packages/plg_system_mokoog/src/Extension/MokoOG.php b/source/packages/plg_system_mokoog/src/Extension/MokoOG.php index bb59693..07c5596 100644 --- a/source/packages/plg_system_mokoog/src/Extension/MokoOG.php +++ b/source/packages/plg_system_mokoog/src/Extension/MokoOG.php @@ -832,7 +832,10 @@ final class MokoOG extends CMSPlugin implements SubscriberInterface $changefreq = $this->params->get('sitemap_changefreq', 'weekly'); $xml = SitemapBuilder::generate($changefreq); - SitemapBuilder::writeToFile($xml); + + if (!SitemapBuilder::writeToFile($xml)) { + \Joomla\CMS\Log\Log::add('MokoOG: Failed to write sitemap.xml — check file permissions', \Joomla\CMS\Log\Log::WARNING, 'mokoog'); + } } /** @@ -850,7 +853,10 @@ final class MokoOG extends CMSPlugin implements SubscriberInterface return; } - \Joomla\CMS\Session\Session::checkToken() or die('Invalid Token'); + if (!\Joomla\CMS\Session\Session::checkToken()) { + $event->setArgument('result', ['Invalid Token']); + return; + } if (!$this->params->get('ai_enabled', 0)) { $event->setArgument('result', ['AI generation is not enabled']); @@ -868,7 +874,7 @@ final class MokoOG extends CMSPlugin implements SubscriberInterface $input = $app->getInput(); $field = $input->getString('field', 'title'); - $articleTitle = $input->getString('article_title', ''); + $articleTitle = mb_substr(strip_tags($input->getString('article_title', '')), 0, 200); $prompt = $field === 'title' ? "Generate a concise, engaging social media sharing title (max 60 characters) for an article titled: \"$articleTitle\". Return only the title text, no quotes or explanation." diff --git a/source/packages/plg_system_mokoog/src/Helper/SitemapBuilder.php b/source/packages/plg_system_mokoog/src/Helper/SitemapBuilder.php index 70068e7..af2149c 100644 --- a/source/packages/plg_system_mokoog/src/Helper/SitemapBuilder.php +++ b/source/packages/plg_system_mokoog/src/Helper/SitemapBuilder.php @@ -32,6 +32,9 @@ class SitemapBuilder */ public static function generate(string $changefreq = 'weekly'): string { + $allowed = ['always', 'hourly', 'daily', 'weekly', 'monthly', 'yearly', 'never']; + $changefreq = \in_array($changefreq, $allowed, true) ? $changefreq : 'weekly'; + $db = Factory::getDbo(); // Get all published articles @@ -51,7 +54,7 @@ class SitemapBuilder ->where($db->quoteName('robots') . ' LIKE ' . $db->quote('%noindex%')); $db->setQuery($noindexQuery); - $noindexIds = $db->loadColumn(); + $noindexIds = array_map('intval', $db->loadColumn()); $root = rtrim(Uri::root(), '/'); $xml = '<?xml version="1.0" encoding="UTF-8"?>' . "\n"; @@ -66,7 +69,7 @@ class SitemapBuilder foreach ($articles as $article) { // Skip noindexed - if (in_array((int) $article->id, $noindexIds)) { + if (\in_array((int) $article->id, $noindexIds, true)) { continue; } From 2907e64641f91482db0548eb880b86ad4dd8957d Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" <gitea-actions[bot]@mokoconsulting.tech> Date: Sun, 28 Jun 2026 18:56:01 +0000 Subject: [PATCH 34/41] chore(version): auto-bump patch 01.04.12-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 2 +- README.md | 2 +- SECURITY.md | 2 +- source/packages/com_mokoog/mokoog.xml | 2 +- source/packages/com_mokoog/sql/updates/mysql/01.04.12.sql | 1 + source/packages/plg_content_mokoog/mokoog.xml | 2 +- source/packages/plg_system_mokoog/mokoog.xml | 2 +- source/packages/plg_webservices_mokoog/mokoog.xml | 2 +- source/pkg_mokoog.xml | 2 +- 10 files changed, 10 insertions(+), 9 deletions(-) create mode 100644 source/packages/com_mokoog/sql/updates/mysql/01.04.12.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 04b599a..da9d64e 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.04.11 +# VERSION: 01.04.12 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c1acf2..6b0fd77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -<!-- VERSION: 01.04.11 --> +<!-- VERSION: 01.04.12 --> All notable changes to MokoSuiteOpenGraph will be documented in this file. diff --git a/README.md b/README.md index 8d2b06e..2392a8b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoSuiteOpenGraph -<!-- VERSION: 01.04.11 --> +<!-- VERSION: 01.04.12 --> Open Graph, Twitter Card, and social sharing meta tag management for Joomla 4/5/6. diff --git a/SECURITY.md b/SECURITY.md index ec2a6d3..37e1512 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 01.04.11 +VERSION: 01.04.12 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/com_mokoog/mokoog.xml b/source/packages/com_mokoog/mokoog.xml index 1bd2511..b2f29a9 100644 --- a/source/packages/com_mokoog/mokoog.xml +++ b/source/packages/com_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="component" method="upgrade"> <name>com_mokoog</name> - <version>01.04.11</version> + <version>01.04.12</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/com_mokoog/sql/updates/mysql/01.04.12.sql b/source/packages/com_mokoog/sql/updates/mysql/01.04.12.sql new file mode 100644 index 0000000..555657a --- /dev/null +++ b/source/packages/com_mokoog/sql/updates/mysql/01.04.12.sql @@ -0,0 +1 @@ +/* 01.04.12 — no schema changes */ diff --git a/source/packages/plg_content_mokoog/mokoog.xml b/source/packages/plg_content_mokoog/mokoog.xml index 5f75895..cbf5f74 100644 --- a/source/packages/plg_content_mokoog/mokoog.xml +++ b/source/packages/plg_content_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="content" method="upgrade"> <name>Content - MokoJoomOpenGraph</name> - <version>01.04.11</version> + <version>01.04.12</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_system_mokoog/mokoog.xml b/source/packages/plg_system_mokoog/mokoog.xml index 70dcb41..693a566 100644 --- a/source/packages/plg_system_mokoog/mokoog.xml +++ b/source/packages/plg_system_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="system" method="upgrade"> <name>System - MokoJoomOpenGraph</name> - <version>01.04.11</version> + <version>01.04.12</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_webservices_mokoog/mokoog.xml b/source/packages/plg_webservices_mokoog/mokoog.xml index b1acbcf..f9b711a 100644 --- a/source/packages/plg_webservices_mokoog/mokoog.xml +++ b/source/packages/plg_webservices_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="webservices" method="upgrade"> <name>Web Services - MokoJoomOpenGraph</name> - <version>01.04.11</version> + <version>01.04.12</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/pkg_mokoog.xml b/source/pkg_mokoog.xml index 22a49f8..1b9653d 100644 --- a/source/pkg_mokoog.xml +++ b/source/pkg_mokoog.xml @@ -8,7 +8,7 @@ <extension type="package" method="upgrade"> <name>Package - MokoSuiteOpenGraph</name> <packagename>mokoog</packagename> - <version>01.04.11</version> + <version>01.04.12</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> From 42ffb4b46cf6d2273ef0660038d235e33fd9acbb Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" <gitea-actions[bot]@mokoconsulting.tech> Date: Sun, 28 Jun 2026 18:56:12 +0000 Subject: [PATCH 35/41] chore(version): pre-release bump to 01.04.13-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 2 +- README.md | 2 +- SECURITY.md | 2 +- source/packages/com_mokoog/mokoog.xml | 2 +- source/packages/com_mokoog/sql/updates/mysql/01.04.13.sql | 1 + source/packages/plg_content_mokoog/mokoog.xml | 2 +- source/packages/plg_system_mokoog/mokoog.xml | 2 +- source/packages/plg_webservices_mokoog/mokoog.xml | 2 +- source/pkg_mokoog.xml | 2 +- 10 files changed, 10 insertions(+), 9 deletions(-) create mode 100644 source/packages/com_mokoog/sql/updates/mysql/01.04.13.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index da9d64e..f8b70b8 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.04.12 +# VERSION: 01.04.13 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b0fd77..1212f39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -<!-- VERSION: 01.04.12 --> +<!-- VERSION: 01.04.13 --> All notable changes to MokoSuiteOpenGraph will be documented in this file. diff --git a/README.md b/README.md index 2392a8b..f378338 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoSuiteOpenGraph -<!-- VERSION: 01.04.12 --> +<!-- VERSION: 01.04.13 --> Open Graph, Twitter Card, and social sharing meta tag management for Joomla 4/5/6. diff --git a/SECURITY.md b/SECURITY.md index 37e1512..fc4b025 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 01.04.12 +VERSION: 01.04.13 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/com_mokoog/mokoog.xml b/source/packages/com_mokoog/mokoog.xml index b2f29a9..9a04990 100644 --- a/source/packages/com_mokoog/mokoog.xml +++ b/source/packages/com_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="component" method="upgrade"> <name>com_mokoog</name> - <version>01.04.12</version> + <version>01.04.13</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/com_mokoog/sql/updates/mysql/01.04.13.sql b/source/packages/com_mokoog/sql/updates/mysql/01.04.13.sql new file mode 100644 index 0000000..529387c --- /dev/null +++ b/source/packages/com_mokoog/sql/updates/mysql/01.04.13.sql @@ -0,0 +1 @@ +/* 01.04.13 — no schema changes */ diff --git a/source/packages/plg_content_mokoog/mokoog.xml b/source/packages/plg_content_mokoog/mokoog.xml index cbf5f74..84a6879 100644 --- a/source/packages/plg_content_mokoog/mokoog.xml +++ b/source/packages/plg_content_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="content" method="upgrade"> <name>Content - MokoJoomOpenGraph</name> - <version>01.04.12</version> + <version>01.04.13</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_system_mokoog/mokoog.xml b/source/packages/plg_system_mokoog/mokoog.xml index 693a566..9c7c585 100644 --- a/source/packages/plg_system_mokoog/mokoog.xml +++ b/source/packages/plg_system_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="system" method="upgrade"> <name>System - MokoJoomOpenGraph</name> - <version>01.04.12</version> + <version>01.04.13</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_webservices_mokoog/mokoog.xml b/source/packages/plg_webservices_mokoog/mokoog.xml index f9b711a..be26877 100644 --- a/source/packages/plg_webservices_mokoog/mokoog.xml +++ b/source/packages/plg_webservices_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="webservices" method="upgrade"> <name>Web Services - MokoJoomOpenGraph</name> - <version>01.04.12</version> + <version>01.04.13</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/pkg_mokoog.xml b/source/pkg_mokoog.xml index 1b9653d..a98a595 100644 --- a/source/pkg_mokoog.xml +++ b/source/pkg_mokoog.xml @@ -8,7 +8,7 @@ <extension type="package" method="upgrade"> <name>Package - MokoSuiteOpenGraph</name> <packagename>mokoog</packagename> - <version>01.04.12</version> + <version>01.04.13</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> From 5ea422d75ed3fc58d3970acc1fe60c79cbf8a94b Mon Sep 17 00:00:00 2001 From: Jonathan Miller <jmiller@noreply.git.mokoconsulting.tech> Date: Sun, 28 Jun 2026 14:19:29 -0500 Subject: [PATCH 36/41] docs(api): require Joomla 6.0+ in OpenAPI metadata --- openapi.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openapi.yaml b/openapi.yaml index c7508b5..4969c24 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -7,6 +7,8 @@ info: REST API for managing Open Graph, SEO meta, and structured-data tags in Joomla via the MokoSuiteOpenGraph extension. + **Requires Joomla 6.0 or higher.** + The API follows Joomla's Web Services conventions and returns responses in [JSON:API](https://jsonapi.org/) format. All endpoints require authentication via a Joomla API token. From 36ce686ae14a7db88e433739e7e7e02e7b68ec42 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" <gitea-actions[bot]@mokoconsulting.tech> Date: Sun, 28 Jun 2026 19:24:54 +0000 Subject: [PATCH 37/41] chore(version): auto-bump patch 01.04.14-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 2 +- CODE_OF_CONDUCT.md | 2 +- GOVERNANCE.md | 2 +- README.md | 2 +- SECURITY.md | 2 +- source/packages/com_mokoog/mokoog.xml | 2 +- source/packages/com_mokoog/sql/updates/mysql/01.04.14.sql | 1 + source/packages/plg_content_mokoog/mokoog.xml | 2 +- source/packages/plg_system_mokoog/mokoog.xml | 2 +- source/packages/plg_webservices_mokoog/mokoog.xml | 2 +- source/pkg_mokoog.xml | 2 +- 12 files changed, 12 insertions(+), 11 deletions(-) create mode 100644 source/packages/com_mokoog/sql/updates/mysql/01.04.14.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 11958bd..ed716b8 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.00.00 +# VERSION: 01.04.14 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index 1212f39..4f6f25f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -<!-- VERSION: 01.04.13 --> +<!-- VERSION: 01.04.14 --> All notable changes to MokoSuiteOpenGraph will be documented in this file. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 809e983..610fd47 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -14,7 +14,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://github.com/mokoconsulting-tech/Template-Joomla/ - VERSION: 01.01.00 + VERSION: 01.04.14 PATH: ./CODE_OF_CONDUCT.md BRIEF: Community expectations and enforcement guidelines NOTE: Adapted with attribution from the Contributor Covenant v2.1 diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 47fa254..cf61eff 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -19,7 +19,7 @@ DEFGROUP: mokoconsulting-tech.Template-Joomla INGROUP: MokoStandards.Governance REPO: https://github.com/mokoconsulting-tech/Template-Joomla - VERSION: 01.01.00 + VERSION: 01.04.14 PATH: /GOVERNANCE.md BRIEF: Project governance rules, roles, and decision process for Template-Joomla --> diff --git a/README.md b/README.md index f378338..d3f932d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoSuiteOpenGraph -<!-- VERSION: 01.04.13 --> +<!-- VERSION: 01.04.14 --> Open Graph, Twitter Card, and social sharing meta tag management for Joomla 4/5/6. diff --git a/SECURITY.md b/SECURITY.md index 86b35ed..2cd8142 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 01.01.00 +VERSION: 01.04.14 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/com_mokoog/mokoog.xml b/source/packages/com_mokoog/mokoog.xml index 9a04990..ca32aea 100644 --- a/source/packages/com_mokoog/mokoog.xml +++ b/source/packages/com_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="component" method="upgrade"> <name>com_mokoog</name> - <version>01.04.13</version> + <version>01.04.14</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/com_mokoog/sql/updates/mysql/01.04.14.sql b/source/packages/com_mokoog/sql/updates/mysql/01.04.14.sql new file mode 100644 index 0000000..617f360 --- /dev/null +++ b/source/packages/com_mokoog/sql/updates/mysql/01.04.14.sql @@ -0,0 +1 @@ +/* 01.04.14 — no schema changes */ diff --git a/source/packages/plg_content_mokoog/mokoog.xml b/source/packages/plg_content_mokoog/mokoog.xml index 84a6879..6897697 100644 --- a/source/packages/plg_content_mokoog/mokoog.xml +++ b/source/packages/plg_content_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="content" method="upgrade"> <name>Content - MokoJoomOpenGraph</name> - <version>01.04.13</version> + <version>01.04.14</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_system_mokoog/mokoog.xml b/source/packages/plg_system_mokoog/mokoog.xml index 9c7c585..6d766f3 100644 --- a/source/packages/plg_system_mokoog/mokoog.xml +++ b/source/packages/plg_system_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="system" method="upgrade"> <name>System - MokoJoomOpenGraph</name> - <version>01.04.13</version> + <version>01.04.14</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_webservices_mokoog/mokoog.xml b/source/packages/plg_webservices_mokoog/mokoog.xml index be26877..a6c6850 100644 --- a/source/packages/plg_webservices_mokoog/mokoog.xml +++ b/source/packages/plg_webservices_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="webservices" method="upgrade"> <name>Web Services - MokoJoomOpenGraph</name> - <version>01.04.13</version> + <version>01.04.14</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/pkg_mokoog.xml b/source/pkg_mokoog.xml index a98a595..0449f8f 100644 --- a/source/pkg_mokoog.xml +++ b/source/pkg_mokoog.xml @@ -8,7 +8,7 @@ <extension type="package" method="upgrade"> <name>Package - MokoSuiteOpenGraph</name> <packagename>mokoog</packagename> - <version>01.04.13</version> + <version>01.04.14</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> From cedf6808d2a0057231d09734104e9dc0310d9a2a Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" <gitea-actions[bot]@mokoconsulting.tech> Date: Sun, 28 Jun 2026 19:25:11 +0000 Subject: [PATCH 38/41] chore(version): pre-release bump to 01.04.15-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 2 +- CODE_OF_CONDUCT.md | 2 +- GOVERNANCE.md | 2 +- README.md | 2 +- SECURITY.md | 2 +- source/packages/com_mokoog/mokoog.xml | 2 +- source/packages/com_mokoog/sql/updates/mysql/01.04.15.sql | 1 + source/packages/plg_content_mokoog/mokoog.xml | 2 +- source/packages/plg_system_mokoog/mokoog.xml | 2 +- source/packages/plg_webservices_mokoog/mokoog.xml | 2 +- source/pkg_mokoog.xml | 2 +- 12 files changed, 12 insertions(+), 11 deletions(-) create mode 100644 source/packages/com_mokoog/sql/updates/mysql/01.04.15.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index ed716b8..ec5a2df 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.04.14 +# VERSION: 01.04.15 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f6f25f..f1d1603 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -<!-- VERSION: 01.04.14 --> +<!-- VERSION: 01.04.15 --> All notable changes to MokoSuiteOpenGraph will be documented in this file. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 610fd47..5f34561 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -14,7 +14,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://github.com/mokoconsulting-tech/Template-Joomla/ - VERSION: 01.04.14 + VERSION: 01.04.15 PATH: ./CODE_OF_CONDUCT.md BRIEF: Community expectations and enforcement guidelines NOTE: Adapted with attribution from the Contributor Covenant v2.1 diff --git a/GOVERNANCE.md b/GOVERNANCE.md index cf61eff..6152d98 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -19,7 +19,7 @@ DEFGROUP: mokoconsulting-tech.Template-Joomla INGROUP: MokoStandards.Governance REPO: https://github.com/mokoconsulting-tech/Template-Joomla - VERSION: 01.04.14 + VERSION: 01.04.15 PATH: /GOVERNANCE.md BRIEF: Project governance rules, roles, and decision process for Template-Joomla --> diff --git a/README.md b/README.md index d3f932d..427cdc3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoSuiteOpenGraph -<!-- VERSION: 01.04.14 --> +<!-- VERSION: 01.04.15 --> Open Graph, Twitter Card, and social sharing meta tag management for Joomla 4/5/6. diff --git a/SECURITY.md b/SECURITY.md index 2cd8142..cb1981f 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 01.04.14 +VERSION: 01.04.15 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/com_mokoog/mokoog.xml b/source/packages/com_mokoog/mokoog.xml index ca32aea..6145539 100644 --- a/source/packages/com_mokoog/mokoog.xml +++ b/source/packages/com_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="component" method="upgrade"> <name>com_mokoog</name> - <version>01.04.14</version> + <version>01.04.15</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/com_mokoog/sql/updates/mysql/01.04.15.sql b/source/packages/com_mokoog/sql/updates/mysql/01.04.15.sql new file mode 100644 index 0000000..2f2ebe4 --- /dev/null +++ b/source/packages/com_mokoog/sql/updates/mysql/01.04.15.sql @@ -0,0 +1 @@ +/* 01.04.15 — no schema changes */ diff --git a/source/packages/plg_content_mokoog/mokoog.xml b/source/packages/plg_content_mokoog/mokoog.xml index 6897697..0f44615 100644 --- a/source/packages/plg_content_mokoog/mokoog.xml +++ b/source/packages/plg_content_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="content" method="upgrade"> <name>Content - MokoJoomOpenGraph</name> - <version>01.04.14</version> + <version>01.04.15</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_system_mokoog/mokoog.xml b/source/packages/plg_system_mokoog/mokoog.xml index 6d766f3..42df4d0 100644 --- a/source/packages/plg_system_mokoog/mokoog.xml +++ b/source/packages/plg_system_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="system" method="upgrade"> <name>System - MokoJoomOpenGraph</name> - <version>01.04.14</version> + <version>01.04.15</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_webservices_mokoog/mokoog.xml b/source/packages/plg_webservices_mokoog/mokoog.xml index a6c6850..c3fdbf0 100644 --- a/source/packages/plg_webservices_mokoog/mokoog.xml +++ b/source/packages/plg_webservices_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="webservices" method="upgrade"> <name>Web Services - MokoJoomOpenGraph</name> - <version>01.04.14</version> + <version>01.04.15</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/pkg_mokoog.xml b/source/pkg_mokoog.xml index 0449f8f..198fb90 100644 --- a/source/pkg_mokoog.xml +++ b/source/pkg_mokoog.xml @@ -8,7 +8,7 @@ <extension type="package" method="upgrade"> <name>Package - MokoSuiteOpenGraph</name> <packagename>mokoog</packagename> - <version>01.04.14</version> + <version>01.04.15</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> From 7fb7e3876276e53caef35805bdf07013b8a14478 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <jmiller@noreply.git.mokoconsulting.tech> Date: Sun, 28 Jun 2026 14:31:18 -0500 Subject: [PATCH 39/41] refactor: rename MokoJoomOpenGraph -> MokoSuiteOpenGraph; require Joomla 6+ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Product rename (display name / docs / comments / language strings only — technical element names mokoog/com_mokoog/MokoOG namespace unchanged): - Replace "MokoJoom" -> "MokoSuite" across 55 files - Fixes the update-site license lookup in script.php, which matched the old "%MokoJoomOpenGraph%" name and would never find a "MokoSuite" site Joomla 6 compatibility: - script.php: minimumJoomla 4.0.0 -> 6.0.0, minimumPhp 8.1.0 -> 8.2.0, and actually enforce the Joomla floor in preflight() (was PHP-only) - Add PKG_MOKOOG_JOOMLA_VERSION_ERROR language strings (en-GB, en-US) - openapi.yaml + README state Joomla 6.0+ requirement - Audit confirmed the codebase already uses only Joomla-6-supported APIs --- .mokogitea/CLAUDE.md | 4 ++-- README.md | 2 +- source/language/en-GB/pkg_mokoog.sys.ini | 7 ++++--- source/language/en-US/pkg_mokoog.sys.ini | 7 ++++--- .../api/src/Controller/TagsController.php | 2 +- .../api/src/View/Tags/JsonapiView.php | 2 +- .../packages/com_mokoog/forms/filter_tags.xml | 2 +- source/packages/com_mokoog/forms/tag.xml | 2 +- .../com_mokoog/language/en-GB/com_mokoog.ini | 6 +++--- .../language/en-GB/com_mokoog.sys.ini | 4 ++-- .../com_mokoog/language/en-US/com_mokoog.ini | 6 +++--- .../language/en-US/com_mokoog.sys.ini | 4 ++-- source/packages/com_mokoog/mokoog.xml | 2 +- source/packages/com_mokoog/script.php | 6 +++--- .../packages/com_mokoog/services/provider.php | 2 +- .../packages/com_mokoog/sql/install.mysql.sql | 2 +- .../com_mokoog/sql/uninstall.mysql.sql | 2 +- .../com_mokoog/sql/updates/mysql/01.01.00.sql | 2 +- .../com_mokoog/sql/updates/mysql/01.02.00.sql | 2 +- .../com_mokoog/sql/updates/mysql/01.03.00.sql | 2 +- .../com_mokoog/sql/updates/mysql/01.04.00.sql | 2 +- .../src/Controller/DisplayController.php | 2 +- .../src/Extension/MokoOGComponent.php | 2 +- .../packages/com_mokoog/src/Model/TagModel.php | 2 +- .../com_mokoog/src/Model/TagsModel.php | 2 +- .../com_mokoog/src/View/Tags/HtmlView.php | 2 +- .../packages/com_mokoog/tmpl/tags/coverage.php | 2 +- .../packages/com_mokoog/tmpl/tags/default.php | 2 +- .../plg_content_mokoog/forms/mokoog.xml | 2 +- .../language/en-GB/plg_content_mokoog.ini | 2 +- .../language/en-GB/plg_content_mokoog.sys.ini | 4 ++-- .../language/en-US/plg_content_mokoog.ini | 2 +- .../language/en-US/plg_content_mokoog.sys.ini | 4 ++-- .../plg_content_mokoog/media/css/preview.css | 2 +- .../plg_content_mokoog/media/joomla.asset.json | 2 +- .../plg_content_mokoog/media/js/preview.js | 2 +- source/packages/plg_content_mokoog/mokoog.php | 2 +- source/packages/plg_content_mokoog/mokoog.xml | 4 ++-- .../plg_content_mokoog/services/provider.php | 2 +- .../src/Extension/MokoOGContent.php | 2 +- .../language/en-GB/plg_system_mokoog.ini | 2 +- .../language/en-GB/plg_system_mokoog.sys.ini | 4 ++-- .../language/en-US/plg_system_mokoog.ini | 2 +- .../language/en-US/plg_system_mokoog.sys.ini | 4 ++-- source/packages/plg_system_mokoog/mokoog.php | 2 +- source/packages/plg_system_mokoog/mokoog.xml | 4 ++-- .../plg_system_mokoog/services/provider.php | 2 +- .../language/en-GB/plg_webservices_mokoog.ini | 4 ++-- .../en-GB/plg_webservices_mokoog.sys.ini | 6 +++--- .../language/en-US/plg_webservices_mokoog.ini | 4 ++-- .../en-US/plg_webservices_mokoog.sys.ini | 6 +++--- .../packages/plg_webservices_mokoog/mokoog.php | 2 +- .../packages/plg_webservices_mokoog/mokoog.xml | 4 ++-- .../services/provider.php | 2 +- .../src/Extension/MokoOGWebServices.php | 4 ++-- source/script.php | 18 ++++++++++++++---- 56 files changed, 98 insertions(+), 86 deletions(-) diff --git a/.mokogitea/CLAUDE.md b/.mokogitea/CLAUDE.md index 8aefbbf..cc3cad6 100644 --- a/.mokogitea/CLAUDE.md +++ b/.mokogitea/CLAUDE.md @@ -1,4 +1,4 @@ -# MokoJoomOpenGraph +# MokoSuiteOpenGraph Open Graph, Twitter Card, and social sharing meta tag management for Joomla. Per-article SEO with auto-generation fallback. @@ -9,7 +9,7 @@ Open Graph, Twitter Card, and social sharing meta tag management for Joomla. Per | **Package** | `pkg_mokoog` | | **Language** | PHP 8.1+ | | **Branch** | develop on `dev`, merge to `main` (protected) | -| **Wiki** | [MokoJoomOpenGraph Wiki](https://git.mokoconsulting.tech/MokoConsulting/MokoJoomOpenGraph/wiki) | +| **Wiki** | [MokoSuiteOpenGraph Wiki](https://git.mokoconsulting.tech/MokoConsulting/MokoSuiteOpenGraph/wiki) | ## Commands diff --git a/README.md b/README.md index 427cdc3..daf29c4 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ <!-- VERSION: 01.04.15 --> -Open Graph, Twitter Card, and social sharing meta tag management for Joomla 4/5/6. +Open Graph, Twitter Card, and social sharing meta tag management for Joomla 6 and higher. ## Overview diff --git a/source/language/en-GB/pkg_mokoog.sys.ini b/source/language/en-GB/pkg_mokoog.sys.ini index 47aa587..0579559 100644 --- a/source/language/en-GB/pkg_mokoog.sys.ini +++ b/source/language/en-GB/pkg_mokoog.sys.ini @@ -1,7 +1,8 @@ -; MokoJoomOpenGraph - Package System Language File +; MokoSuiteOpenGraph - Package System Language File ; Copyright (C) 2026 Moko Consulting. All rights reserved. ; License: GPL-3.0-or-later -PKG_MOKOOG="MokoJoomOpenGraph" +PKG_MOKOOG="MokoSuiteOpenGraph" PKG_MOKOOG_DESCRIPTION="Complete Open Graph, Twitter Card, and social sharing meta tag management for Joomla. Control how every page appears when shared on Facebook, Twitter/X, LinkedIn, WhatsApp, and more." -PKG_MOKOOG_PHP_VERSION_ERROR="MokoJoomOpenGraph requires PHP %s or later." +PKG_MOKOOG_PHP_VERSION_ERROR="MokoSuiteOpenGraph requires PHP %s or later." +PKG_MOKOOG_JOOMLA_VERSION_ERROR="MokoSuiteOpenGraph requires Joomla %s or later." diff --git a/source/language/en-US/pkg_mokoog.sys.ini b/source/language/en-US/pkg_mokoog.sys.ini index 47aa587..0579559 100644 --- a/source/language/en-US/pkg_mokoog.sys.ini +++ b/source/language/en-US/pkg_mokoog.sys.ini @@ -1,7 +1,8 @@ -; MokoJoomOpenGraph - Package System Language File +; MokoSuiteOpenGraph - Package System Language File ; Copyright (C) 2026 Moko Consulting. All rights reserved. ; License: GPL-3.0-or-later -PKG_MOKOOG="MokoJoomOpenGraph" +PKG_MOKOOG="MokoSuiteOpenGraph" PKG_MOKOOG_DESCRIPTION="Complete Open Graph, Twitter Card, and social sharing meta tag management for Joomla. Control how every page appears when shared on Facebook, Twitter/X, LinkedIn, WhatsApp, and more." -PKG_MOKOOG_PHP_VERSION_ERROR="MokoJoomOpenGraph requires PHP %s or later." +PKG_MOKOOG_PHP_VERSION_ERROR="MokoSuiteOpenGraph requires PHP %s or later." +PKG_MOKOOG_JOOMLA_VERSION_ERROR="MokoSuiteOpenGraph requires Joomla %s or later." diff --git a/source/packages/com_mokoog/api/src/Controller/TagsController.php b/source/packages/com_mokoog/api/src/Controller/TagsController.php index 9fbe168..2e62de3 100644 --- a/source/packages/com_mokoog/api/src/Controller/TagsController.php +++ b/source/packages/com_mokoog/api/src/Controller/TagsController.php @@ -1,7 +1,7 @@ <?php /** - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @subpackage com_mokoog.api * @author Moko Consulting <hello@mokoconsulting.tech> * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. diff --git a/source/packages/com_mokoog/api/src/View/Tags/JsonapiView.php b/source/packages/com_mokoog/api/src/View/Tags/JsonapiView.php index 97a0aa2..4a33148 100644 --- a/source/packages/com_mokoog/api/src/View/Tags/JsonapiView.php +++ b/source/packages/com_mokoog/api/src/View/Tags/JsonapiView.php @@ -1,7 +1,7 @@ <?php /** - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @subpackage com_mokoog.api * @author Moko Consulting <hello@mokoconsulting.tech> * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. diff --git a/source/packages/com_mokoog/forms/filter_tags.xml b/source/packages/com_mokoog/forms/filter_tags.xml index b4d4015..ae32efc 100644 --- a/source/packages/com_mokoog/forms/filter_tags.xml +++ b/source/packages/com_mokoog/forms/filter_tags.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @subpackage com_mokoog * @author Moko Consulting <hello@mokoconsulting.tech> * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. diff --git a/source/packages/com_mokoog/forms/tag.xml b/source/packages/com_mokoog/forms/tag.xml index 9eb8168..7b4a35f 100644 --- a/source/packages/com_mokoog/forms/tag.xml +++ b/source/packages/com_mokoog/forms/tag.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @subpackage com_mokoog * @author Moko Consulting <hello@mokoconsulting.tech> * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. diff --git a/source/packages/com_mokoog/language/en-GB/com_mokoog.ini b/source/packages/com_mokoog/language/en-GB/com_mokoog.ini index 4e8f84a..4f0d4d3 100644 --- a/source/packages/com_mokoog/language/en-GB/com_mokoog.ini +++ b/source/packages/com_mokoog/language/en-GB/com_mokoog.ini @@ -1,9 +1,9 @@ -; MokoJoomOpenGraph - Component Language File +; MokoSuiteOpenGraph - Component Language File ; Copyright (C) 2026 Moko Consulting. All rights reserved. ; License: GPL-3.0-or-later -COM_MOKOOG="MokoJoomOpenGraph" -COM_MOKOOG_TAGS_TITLE="MokoJoomOpenGraph - Tag Manager" +COM_MOKOOG="MokoSuiteOpenGraph" +COM_MOKOOG_TAGS_TITLE="MokoSuiteOpenGraph - Tag Manager" COM_MOKOOG_SUBMENU_TAGS="Tags" COM_MOKOOG_NO_TAGS="No Open Graph tags have been created yet. Tags are created automatically when you edit articles or menu items." COM_MOKOOG_TABLE_CAPTION="Table of Open Graph tags" diff --git a/source/packages/com_mokoog/language/en-GB/com_mokoog.sys.ini b/source/packages/com_mokoog/language/en-GB/com_mokoog.sys.ini index 0aacbc4..0e382dd 100644 --- a/source/packages/com_mokoog/language/en-GB/com_mokoog.sys.ini +++ b/source/packages/com_mokoog/language/en-GB/com_mokoog.sys.ini @@ -1,6 +1,6 @@ -; MokoJoomOpenGraph - Component System Language File +; MokoSuiteOpenGraph - Component System Language File ; Copyright (C) 2026 Moko Consulting. All rights reserved. ; License: GPL-3.0-or-later -COM_MOKOOG="MokoJoomOpenGraph" +COM_MOKOOG="MokoSuiteOpenGraph" COM_MOKOOG_DESCRIPTION="Manage Open Graph and social sharing tags for all your content. View, edit, and batch-process OG metadata." diff --git a/source/packages/com_mokoog/language/en-US/com_mokoog.ini b/source/packages/com_mokoog/language/en-US/com_mokoog.ini index 4e8f84a..4f0d4d3 100644 --- a/source/packages/com_mokoog/language/en-US/com_mokoog.ini +++ b/source/packages/com_mokoog/language/en-US/com_mokoog.ini @@ -1,9 +1,9 @@ -; MokoJoomOpenGraph - Component Language File +; MokoSuiteOpenGraph - Component Language File ; Copyright (C) 2026 Moko Consulting. All rights reserved. ; License: GPL-3.0-or-later -COM_MOKOOG="MokoJoomOpenGraph" -COM_MOKOOG_TAGS_TITLE="MokoJoomOpenGraph - Tag Manager" +COM_MOKOOG="MokoSuiteOpenGraph" +COM_MOKOOG_TAGS_TITLE="MokoSuiteOpenGraph - Tag Manager" COM_MOKOOG_SUBMENU_TAGS="Tags" COM_MOKOOG_NO_TAGS="No Open Graph tags have been created yet. Tags are created automatically when you edit articles or menu items." COM_MOKOOG_TABLE_CAPTION="Table of Open Graph tags" diff --git a/source/packages/com_mokoog/language/en-US/com_mokoog.sys.ini b/source/packages/com_mokoog/language/en-US/com_mokoog.sys.ini index 0aacbc4..0e382dd 100644 --- a/source/packages/com_mokoog/language/en-US/com_mokoog.sys.ini +++ b/source/packages/com_mokoog/language/en-US/com_mokoog.sys.ini @@ -1,6 +1,6 @@ -; MokoJoomOpenGraph - Component System Language File +; MokoSuiteOpenGraph - Component System Language File ; Copyright (C) 2026 Moko Consulting. All rights reserved. ; License: GPL-3.0-or-later -COM_MOKOOG="MokoJoomOpenGraph" +COM_MOKOOG="MokoSuiteOpenGraph" COM_MOKOOG_DESCRIPTION="Manage Open Graph and social sharing tags for all your content. View, edit, and batch-process OG metadata." diff --git a/source/packages/com_mokoog/mokoog.xml b/source/packages/com_mokoog/mokoog.xml index 6145539..c0c7106 100644 --- a/source/packages/com_mokoog/mokoog.xml +++ b/source/packages/com_mokoog/mokoog.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @subpackage com_mokoog * @author Moko Consulting <hello@mokoconsulting.tech> * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. diff --git a/source/packages/com_mokoog/script.php b/source/packages/com_mokoog/script.php index 5073df5..ace5636 100644 --- a/source/packages/com_mokoog/script.php +++ b/source/packages/com_mokoog/script.php @@ -1,7 +1,7 @@ <?php /** - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @subpackage com_mokoog * @author Moko Consulting <hello@mokoconsulting.tech> * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. @@ -23,7 +23,7 @@ class Com_MokoOGInstallerScript */ public function install(InstallerAdapter $parent): void { - echo '<p>MokoJoomOpenGraph component installed successfully.</p>'; + echo '<p>MokoSuiteOpenGraph component installed successfully.</p>'; } /** @@ -35,6 +35,6 @@ class Com_MokoOGInstallerScript */ public function update(InstallerAdapter $parent): void { - echo '<p>MokoJoomOpenGraph component updated successfully.</p>'; + echo '<p>MokoSuiteOpenGraph component updated successfully.</p>'; } } diff --git a/source/packages/com_mokoog/services/provider.php b/source/packages/com_mokoog/services/provider.php index 27c3354..af865fc 100644 --- a/source/packages/com_mokoog/services/provider.php +++ b/source/packages/com_mokoog/services/provider.php @@ -1,7 +1,7 @@ <?php /** - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @subpackage com_mokoog * @author Moko Consulting <hello@mokoconsulting.tech> * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. diff --git a/source/packages/com_mokoog/sql/install.mysql.sql b/source/packages/com_mokoog/sql/install.mysql.sql index ac3112c..524cd99 100644 --- a/source/packages/com_mokoog/sql/install.mysql.sql +++ b/source/packages/com_mokoog/sql/install.mysql.sql @@ -1,5 +1,5 @@ -- --- MokoJoomOpenGraph - Database Schema +-- MokoSuiteOpenGraph - Database Schema -- Copyright (C) 2026 Moko Consulting. All rights reserved. -- License: GPL-3.0-or-later -- diff --git a/source/packages/com_mokoog/sql/uninstall.mysql.sql b/source/packages/com_mokoog/sql/uninstall.mysql.sql index 8652ac6..cd243a3 100644 --- a/source/packages/com_mokoog/sql/uninstall.mysql.sql +++ b/source/packages/com_mokoog/sql/uninstall.mysql.sql @@ -1,5 +1,5 @@ -- --- MokoJoomOpenGraph - Uninstall +-- MokoSuiteOpenGraph - Uninstall -- DROP TABLE IF EXISTS `#__mokoog_tags`; diff --git a/source/packages/com_mokoog/sql/updates/mysql/01.01.00.sql b/source/packages/com_mokoog/sql/updates/mysql/01.01.00.sql index 5bac763..c9d404a 100644 --- a/source/packages/com_mokoog/sql/updates/mysql/01.01.00.sql +++ b/source/packages/com_mokoog/sql/updates/mysql/01.01.00.sql @@ -1,5 +1,5 @@ -- --- MokoJoomOpenGraph 01.01.00 — Add SEO meta management columns +-- MokoSuiteOpenGraph 01.01.00 — Add SEO meta management columns -- ALTER TABLE `#__mokoog_tags` diff --git a/source/packages/com_mokoog/sql/updates/mysql/01.02.00.sql b/source/packages/com_mokoog/sql/updates/mysql/01.02.00.sql index ca87208..8404704 100644 --- a/source/packages/com_mokoog/sql/updates/mysql/01.02.00.sql +++ b/source/packages/com_mokoog/sql/updates/mysql/01.02.00.sql @@ -1,5 +1,5 @@ -- --- MokoJoomOpenGraph 01.02.00 — Add multilingual OG tag support +-- MokoSuiteOpenGraph 01.02.00 — Add multilingual OG tag support -- ALTER TABLE `#__mokoog_tags` diff --git a/source/packages/com_mokoog/sql/updates/mysql/01.03.00.sql b/source/packages/com_mokoog/sql/updates/mysql/01.03.00.sql index 6d21c00..f5797f4 100644 --- a/source/packages/com_mokoog/sql/updates/mysql/01.03.00.sql +++ b/source/packages/com_mokoog/sql/updates/mysql/01.03.00.sql @@ -1,5 +1,5 @@ -- --- MokoJoomOpenGraph 01.03.00 - Add og_video column +-- MokoSuiteOpenGraph 01.03.00 - Add og_video column -- ALTER TABLE `#__mokoog_tags` ADD COLUMN `og_video` VARCHAR(512) NOT NULL DEFAULT '' AFTER `og_type`; diff --git a/source/packages/com_mokoog/sql/updates/mysql/01.04.00.sql b/source/packages/com_mokoog/sql/updates/mysql/01.04.00.sql index 225089e..5049929 100644 --- a/source/packages/com_mokoog/sql/updates/mysql/01.04.00.sql +++ b/source/packages/com_mokoog/sql/updates/mysql/01.04.00.sql @@ -1,5 +1,5 @@ -- --- MokoJoomOpenGraph 01.04.00 - Add event_data and recipe_data columns +-- MokoSuiteOpenGraph 01.04.00 - Add event_data and recipe_data columns -- ALTER TABLE `#__mokoog_tags` ADD COLUMN `event_data` TEXT NULL AFTER `og_video`; diff --git a/source/packages/com_mokoog/src/Controller/DisplayController.php b/source/packages/com_mokoog/src/Controller/DisplayController.php index 28b73c9..93330a7 100644 --- a/source/packages/com_mokoog/src/Controller/DisplayController.php +++ b/source/packages/com_mokoog/src/Controller/DisplayController.php @@ -1,7 +1,7 @@ <?php /** - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @subpackage com_mokoog * @author Moko Consulting <hello@mokoconsulting.tech> * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. diff --git a/source/packages/com_mokoog/src/Extension/MokoOGComponent.php b/source/packages/com_mokoog/src/Extension/MokoOGComponent.php index f8e3a9e..5262b25 100644 --- a/source/packages/com_mokoog/src/Extension/MokoOGComponent.php +++ b/source/packages/com_mokoog/src/Extension/MokoOGComponent.php @@ -1,7 +1,7 @@ <?php /** - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @subpackage com_mokoog * @author Moko Consulting <hello@mokoconsulting.tech> * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. diff --git a/source/packages/com_mokoog/src/Model/TagModel.php b/source/packages/com_mokoog/src/Model/TagModel.php index c56b682..04a9c75 100644 --- a/source/packages/com_mokoog/src/Model/TagModel.php +++ b/source/packages/com_mokoog/src/Model/TagModel.php @@ -1,7 +1,7 @@ <?php /** - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @subpackage com_mokoog * @author Moko Consulting <hello@mokoconsulting.tech> * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. diff --git a/source/packages/com_mokoog/src/Model/TagsModel.php b/source/packages/com_mokoog/src/Model/TagsModel.php index 4171c21..81d7f23 100644 --- a/source/packages/com_mokoog/src/Model/TagsModel.php +++ b/source/packages/com_mokoog/src/Model/TagsModel.php @@ -1,7 +1,7 @@ <?php /** - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @subpackage com_mokoog * @author Moko Consulting <hello@mokoconsulting.tech> * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. diff --git a/source/packages/com_mokoog/src/View/Tags/HtmlView.php b/source/packages/com_mokoog/src/View/Tags/HtmlView.php index eca4100..9a07a00 100644 --- a/source/packages/com_mokoog/src/View/Tags/HtmlView.php +++ b/source/packages/com_mokoog/src/View/Tags/HtmlView.php @@ -1,7 +1,7 @@ <?php /** - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @subpackage com_mokoog * @author Moko Consulting <hello@mokoconsulting.tech> * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. diff --git a/source/packages/com_mokoog/tmpl/tags/coverage.php b/source/packages/com_mokoog/tmpl/tags/coverage.php index b2f0830..eb607b3 100644 --- a/source/packages/com_mokoog/tmpl/tags/coverage.php +++ b/source/packages/com_mokoog/tmpl/tags/coverage.php @@ -1,7 +1,7 @@ <?php /** - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @subpackage com_mokoog * @author Moko Consulting <hello@mokoconsulting.tech> * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. diff --git a/source/packages/com_mokoog/tmpl/tags/default.php b/source/packages/com_mokoog/tmpl/tags/default.php index 0367944..f3a3ad0 100644 --- a/source/packages/com_mokoog/tmpl/tags/default.php +++ b/source/packages/com_mokoog/tmpl/tags/default.php @@ -1,7 +1,7 @@ <?php /** - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @subpackage com_mokoog * @author Moko Consulting <hello@mokoconsulting.tech> * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. diff --git a/source/packages/plg_content_mokoog/forms/mokoog.xml b/source/packages/plg_content_mokoog/forms/mokoog.xml index 6dd2811..f34ee65 100644 --- a/source/packages/plg_content_mokoog/forms/mokoog.xml +++ b/source/packages/plg_content_mokoog/forms/mokoog.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @subpackage plg_content_mokoog * @author Moko Consulting <hello@mokoconsulting.tech> * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. diff --git a/source/packages/plg_content_mokoog/language/en-GB/plg_content_mokoog.ini b/source/packages/plg_content_mokoog/language/en-GB/plg_content_mokoog.ini index 1ade0e2..ffa9954 100644 --- a/source/packages/plg_content_mokoog/language/en-GB/plg_content_mokoog.ini +++ b/source/packages/plg_content_mokoog/language/en-GB/plg_content_mokoog.ini @@ -1,4 +1,4 @@ -; MokoJoomOpenGraph - Content Plugin Language File +; MokoSuiteOpenGraph - Content Plugin Language File ; Copyright (C) 2026 Moko Consulting. All rights reserved. ; License: GPL-3.0-or-later diff --git a/source/packages/plg_content_mokoog/language/en-GB/plg_content_mokoog.sys.ini b/source/packages/plg_content_mokoog/language/en-GB/plg_content_mokoog.sys.ini index 5d16a1b..51e1f35 100644 --- a/source/packages/plg_content_mokoog/language/en-GB/plg_content_mokoog.sys.ini +++ b/source/packages/plg_content_mokoog/language/en-GB/plg_content_mokoog.sys.ini @@ -1,6 +1,6 @@ -; MokoJoomOpenGraph - Content Plugin System Language File +; MokoSuiteOpenGraph - Content Plugin System Language File ; Copyright (C) 2026 Moko Consulting. All rights reserved. ; License: GPL-3.0-or-later -PLG_CONTENT_MOKOOG="Content - MokoJoomOpenGraph" +PLG_CONTENT_MOKOOG="Content - MokoSuiteOpenGraph" PLG_CONTENT_MOKOOG_DESCRIPTION="Adds Open Graph fields to article and menu item edit forms for per-page social sharing control." diff --git a/source/packages/plg_content_mokoog/language/en-US/plg_content_mokoog.ini b/source/packages/plg_content_mokoog/language/en-US/plg_content_mokoog.ini index 1ade0e2..ffa9954 100644 --- a/source/packages/plg_content_mokoog/language/en-US/plg_content_mokoog.ini +++ b/source/packages/plg_content_mokoog/language/en-US/plg_content_mokoog.ini @@ -1,4 +1,4 @@ -; MokoJoomOpenGraph - Content Plugin Language File +; MokoSuiteOpenGraph - Content Plugin Language File ; Copyright (C) 2026 Moko Consulting. All rights reserved. ; License: GPL-3.0-or-later diff --git a/source/packages/plg_content_mokoog/language/en-US/plg_content_mokoog.sys.ini b/source/packages/plg_content_mokoog/language/en-US/plg_content_mokoog.sys.ini index 5d16a1b..51e1f35 100644 --- a/source/packages/plg_content_mokoog/language/en-US/plg_content_mokoog.sys.ini +++ b/source/packages/plg_content_mokoog/language/en-US/plg_content_mokoog.sys.ini @@ -1,6 +1,6 @@ -; MokoJoomOpenGraph - Content Plugin System Language File +; MokoSuiteOpenGraph - Content Plugin System Language File ; Copyright (C) 2026 Moko Consulting. All rights reserved. ; License: GPL-3.0-or-later -PLG_CONTENT_MOKOOG="Content - MokoJoomOpenGraph" +PLG_CONTENT_MOKOOG="Content - MokoSuiteOpenGraph" PLG_CONTENT_MOKOOG_DESCRIPTION="Adds Open Graph fields to article and menu item edit forms for per-page social sharing control." diff --git a/source/packages/plg_content_mokoog/media/css/preview.css b/source/packages/plg_content_mokoog/media/css/preview.css index 346f8e8..6154e26 100644 --- a/source/packages/plg_content_mokoog/media/css/preview.css +++ b/source/packages/plg_content_mokoog/media/css/preview.css @@ -1,5 +1,5 @@ /** - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @subpackage plg_content_mokoog * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GPL-3.0-or-later diff --git a/source/packages/plg_content_mokoog/media/joomla.asset.json b/source/packages/plg_content_mokoog/media/joomla.asset.json index 626a0fc..81340aa 100644 --- a/source/packages/plg_content_mokoog/media/joomla.asset.json +++ b/source/packages/plg_content_mokoog/media/joomla.asset.json @@ -2,7 +2,7 @@ "$schema": "https://developer.joomla.org/schemas/json-schema/web_assets.json", "name": "plg_content_mokoog", "version": "01.00.00", - "description": "MokoJoomOpenGraph Content Plugin Assets", + "description": "MokoSuiteOpenGraph Content Plugin Assets", "license": "GPL-3.0-or-later", "assets": [ { diff --git a/source/packages/plg_content_mokoog/media/js/preview.js b/source/packages/plg_content_mokoog/media/js/preview.js index 9cbc401..46be896 100644 --- a/source/packages/plg_content_mokoog/media/js/preview.js +++ b/source/packages/plg_content_mokoog/media/js/preview.js @@ -1,5 +1,5 @@ /** - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @subpackage plg_content_mokoog * @author Moko Consulting <hello@mokoconsulting.tech> * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. diff --git a/source/packages/plg_content_mokoog/mokoog.php b/source/packages/plg_content_mokoog/mokoog.php index 0f81e14..781f194 100644 --- a/source/packages/plg_content_mokoog/mokoog.php +++ b/source/packages/plg_content_mokoog/mokoog.php @@ -1,7 +1,7 @@ <?php /** - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @subpackage plg_content_mokoog * @author Moko Consulting <hello@mokoconsulting.tech> * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. diff --git a/source/packages/plg_content_mokoog/mokoog.xml b/source/packages/plg_content_mokoog/mokoog.xml index 0f44615..8c2a876 100644 --- a/source/packages/plg_content_mokoog/mokoog.xml +++ b/source/packages/plg_content_mokoog/mokoog.xml @@ -1,13 +1,13 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @subpackage plg_content_mokoog * @author Moko Consulting <hello@mokoconsulting.tech> * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE --> <extension type="plugin" group="content" method="upgrade"> - <name>Content - MokoJoomOpenGraph</name> + <name>Content - MokoSuiteOpenGraph</name> <version>01.04.15</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> diff --git a/source/packages/plg_content_mokoog/services/provider.php b/source/packages/plg_content_mokoog/services/provider.php index aca7a7d..be5bb48 100644 --- a/source/packages/plg_content_mokoog/services/provider.php +++ b/source/packages/plg_content_mokoog/services/provider.php @@ -1,7 +1,7 @@ <?php /** - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @subpackage plg_content_mokoog * @author Moko Consulting <hello@mokoconsulting.tech> * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. diff --git a/source/packages/plg_content_mokoog/src/Extension/MokoOGContent.php b/source/packages/plg_content_mokoog/src/Extension/MokoOGContent.php index 6f55213..15f2db0 100644 --- a/source/packages/plg_content_mokoog/src/Extension/MokoOGContent.php +++ b/source/packages/plg_content_mokoog/src/Extension/MokoOGContent.php @@ -1,7 +1,7 @@ <?php /** - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @subpackage plg_content_mokoog * @author Moko Consulting <hello@mokoconsulting.tech> * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. 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 3791562..7a8f041 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 @@ -1,4 +1,4 @@ -; MokoJoomOpenGraph - System Plugin Language File +; MokoSuiteOpenGraph - System Plugin Language File ; Copyright (C) 2026 Moko Consulting. All rights reserved. ; License: GPL-3.0-or-later diff --git a/source/packages/plg_system_mokoog/language/en-GB/plg_system_mokoog.sys.ini b/source/packages/plg_system_mokoog/language/en-GB/plg_system_mokoog.sys.ini index 2a356e2..5b3afc6 100644 --- a/source/packages/plg_system_mokoog/language/en-GB/plg_system_mokoog.sys.ini +++ b/source/packages/plg_system_mokoog/language/en-GB/plg_system_mokoog.sys.ini @@ -1,6 +1,6 @@ -; MokoJoomOpenGraph - System Plugin System Language File +; MokoSuiteOpenGraph - System Plugin System Language File ; Copyright (C) 2026 Moko Consulting. All rights reserved. ; License: GPL-3.0-or-later -PLG_SYSTEM_MOKOOG="System - MokoJoomOpenGraph" +PLG_SYSTEM_MOKOOG="System - MokoSuiteOpenGraph" PLG_SYSTEM_MOKOOG_DESCRIPTION="Injects Open Graph and Twitter Card meta tags into every page for optimal social media sharing previews." 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 3791562..7a8f041 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 @@ -1,4 +1,4 @@ -; MokoJoomOpenGraph - System Plugin Language File +; MokoSuiteOpenGraph - System Plugin Language File ; Copyright (C) 2026 Moko Consulting. All rights reserved. ; License: GPL-3.0-or-later diff --git a/source/packages/plg_system_mokoog/language/en-US/plg_system_mokoog.sys.ini b/source/packages/plg_system_mokoog/language/en-US/plg_system_mokoog.sys.ini index 2a356e2..5b3afc6 100644 --- a/source/packages/plg_system_mokoog/language/en-US/plg_system_mokoog.sys.ini +++ b/source/packages/plg_system_mokoog/language/en-US/plg_system_mokoog.sys.ini @@ -1,6 +1,6 @@ -; MokoJoomOpenGraph - System Plugin System Language File +; MokoSuiteOpenGraph - System Plugin System Language File ; Copyright (C) 2026 Moko Consulting. All rights reserved. ; License: GPL-3.0-or-later -PLG_SYSTEM_MOKOOG="System - MokoJoomOpenGraph" +PLG_SYSTEM_MOKOOG="System - MokoSuiteOpenGraph" PLG_SYSTEM_MOKOOG_DESCRIPTION="Injects Open Graph and Twitter Card meta tags into every page for optimal social media sharing previews." diff --git a/source/packages/plg_system_mokoog/mokoog.php b/source/packages/plg_system_mokoog/mokoog.php index bfc5577..9e8a445 100644 --- a/source/packages/plg_system_mokoog/mokoog.php +++ b/source/packages/plg_system_mokoog/mokoog.php @@ -1,7 +1,7 @@ <?php /** - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @subpackage plg_system_mokoog * @author Moko Consulting <hello@mokoconsulting.tech> * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. diff --git a/source/packages/plg_system_mokoog/mokoog.xml b/source/packages/plg_system_mokoog/mokoog.xml index 42df4d0..7479263 100644 --- a/source/packages/plg_system_mokoog/mokoog.xml +++ b/source/packages/plg_system_mokoog/mokoog.xml @@ -1,13 +1,13 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @subpackage plg_system_mokoog * @author Moko Consulting <hello@mokoconsulting.tech> * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE --> <extension type="plugin" group="system" method="upgrade"> - <name>System - MokoJoomOpenGraph</name> + <name>System - MokoSuiteOpenGraph</name> <version>01.04.15</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> diff --git a/source/packages/plg_system_mokoog/services/provider.php b/source/packages/plg_system_mokoog/services/provider.php index 390b1d3..e398480 100644 --- a/source/packages/plg_system_mokoog/services/provider.php +++ b/source/packages/plg_system_mokoog/services/provider.php @@ -1,7 +1,7 @@ <?php /** - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @subpackage plg_system_mokoog * @author Moko Consulting <hello@mokoconsulting.tech> * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. diff --git a/source/packages/plg_webservices_mokoog/language/en-GB/plg_webservices_mokoog.ini b/source/packages/plg_webservices_mokoog/language/en-GB/plg_webservices_mokoog.ini index 97f99e3..23b9cd1 100644 --- a/source/packages/plg_webservices_mokoog/language/en-GB/plg_webservices_mokoog.ini +++ b/source/packages/plg_webservices_mokoog/language/en-GB/plg_webservices_mokoog.ini @@ -1,5 +1,5 @@ -; MokoJoomOpenGraph - Web Services Plugin Language File +; MokoSuiteOpenGraph - Web Services Plugin Language File ; Copyright (C) 2026 Moko Consulting. All rights reserved. ; License: GPL-3.0-or-later -PLG_WEBSERVICES_MOKOOG="Web Services - MokoJoomOpenGraph" +PLG_WEBSERVICES_MOKOOG="Web Services - MokoSuiteOpenGraph" diff --git a/source/packages/plg_webservices_mokoog/language/en-GB/plg_webservices_mokoog.sys.ini b/source/packages/plg_webservices_mokoog/language/en-GB/plg_webservices_mokoog.sys.ini index 086ffe8..8d41317 100644 --- a/source/packages/plg_webservices_mokoog/language/en-GB/plg_webservices_mokoog.sys.ini +++ b/source/packages/plg_webservices_mokoog/language/en-GB/plg_webservices_mokoog.sys.ini @@ -1,6 +1,6 @@ -; MokoJoomOpenGraph - Web Services Plugin System Language File +; MokoSuiteOpenGraph - Web Services Plugin System Language File ; Copyright (C) 2026 Moko Consulting. All rights reserved. ; License: GPL-3.0-or-later -PLG_WEBSERVICES_MOKOOG="Web Services - MokoJoomOpenGraph" -PLG_WEBSERVICES_MOKOOG_DESCRIPTION="Exposes MokoJoomOpenGraph OG tag data via Joomla's REST API at /api/index.php/v1/mokoog/tags." +PLG_WEBSERVICES_MOKOOG="Web Services - MokoSuiteOpenGraph" +PLG_WEBSERVICES_MOKOOG_DESCRIPTION="Exposes MokoSuiteOpenGraph OG tag data via Joomla's REST API at /api/index.php/v1/mokoog/tags." diff --git a/source/packages/plg_webservices_mokoog/language/en-US/plg_webservices_mokoog.ini b/source/packages/plg_webservices_mokoog/language/en-US/plg_webservices_mokoog.ini index 97f99e3..23b9cd1 100644 --- a/source/packages/plg_webservices_mokoog/language/en-US/plg_webservices_mokoog.ini +++ b/source/packages/plg_webservices_mokoog/language/en-US/plg_webservices_mokoog.ini @@ -1,5 +1,5 @@ -; MokoJoomOpenGraph - Web Services Plugin Language File +; MokoSuiteOpenGraph - Web Services Plugin Language File ; Copyright (C) 2026 Moko Consulting. All rights reserved. ; License: GPL-3.0-or-later -PLG_WEBSERVICES_MOKOOG="Web Services - MokoJoomOpenGraph" +PLG_WEBSERVICES_MOKOOG="Web Services - MokoSuiteOpenGraph" diff --git a/source/packages/plg_webservices_mokoog/language/en-US/plg_webservices_mokoog.sys.ini b/source/packages/plg_webservices_mokoog/language/en-US/plg_webservices_mokoog.sys.ini index 086ffe8..8d41317 100644 --- a/source/packages/plg_webservices_mokoog/language/en-US/plg_webservices_mokoog.sys.ini +++ b/source/packages/plg_webservices_mokoog/language/en-US/plg_webservices_mokoog.sys.ini @@ -1,6 +1,6 @@ -; MokoJoomOpenGraph - Web Services Plugin System Language File +; MokoSuiteOpenGraph - Web Services Plugin System Language File ; Copyright (C) 2026 Moko Consulting. All rights reserved. ; License: GPL-3.0-or-later -PLG_WEBSERVICES_MOKOOG="Web Services - MokoJoomOpenGraph" -PLG_WEBSERVICES_MOKOOG_DESCRIPTION="Exposes MokoJoomOpenGraph OG tag data via Joomla's REST API at /api/index.php/v1/mokoog/tags." +PLG_WEBSERVICES_MOKOOG="Web Services - MokoSuiteOpenGraph" +PLG_WEBSERVICES_MOKOOG_DESCRIPTION="Exposes MokoSuiteOpenGraph OG tag data via Joomla's REST API at /api/index.php/v1/mokoog/tags." diff --git a/source/packages/plg_webservices_mokoog/mokoog.php b/source/packages/plg_webservices_mokoog/mokoog.php index 945c592..2b3765d 100644 --- a/source/packages/plg_webservices_mokoog/mokoog.php +++ b/source/packages/plg_webservices_mokoog/mokoog.php @@ -1,7 +1,7 @@ <?php /** - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @subpackage plg_webservices_mokoog * @author Moko Consulting <hello@mokoconsulting.tech> * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. diff --git a/source/packages/plg_webservices_mokoog/mokoog.xml b/source/packages/plg_webservices_mokoog/mokoog.xml index c3fdbf0..bfbc862 100644 --- a/source/packages/plg_webservices_mokoog/mokoog.xml +++ b/source/packages/plg_webservices_mokoog/mokoog.xml @@ -1,13 +1,13 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @subpackage plg_webservices_mokoog * @author Moko Consulting <hello@mokoconsulting.tech> * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE --> <extension type="plugin" group="webservices" method="upgrade"> - <name>Web Services - MokoJoomOpenGraph</name> + <name>Web Services - MokoSuiteOpenGraph</name> <version>01.04.15</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> diff --git a/source/packages/plg_webservices_mokoog/services/provider.php b/source/packages/plg_webservices_mokoog/services/provider.php index be36a42..72b352e 100644 --- a/source/packages/plg_webservices_mokoog/services/provider.php +++ b/source/packages/plg_webservices_mokoog/services/provider.php @@ -1,7 +1,7 @@ <?php /** - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @subpackage plg_webservices_mokoog * @author Moko Consulting <hello@mokoconsulting.tech> * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. diff --git a/source/packages/plg_webservices_mokoog/src/Extension/MokoOGWebServices.php b/source/packages/plg_webservices_mokoog/src/Extension/MokoOGWebServices.php index b5cc48f..9fe7a1e 100644 --- a/source/packages/plg_webservices_mokoog/src/Extension/MokoOGWebServices.php +++ b/source/packages/plg_webservices_mokoog/src/Extension/MokoOGWebServices.php @@ -1,7 +1,7 @@ <?php /** - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @subpackage plg_webservices_mokoog * @author Moko Consulting <hello@mokoconsulting.tech> * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. @@ -38,7 +38,7 @@ final class MokoOGWebServices extends CMSPlugin implements SubscriberInterface } /** - * Register API routes for MokoJoomOpenGraph. + * Register API routes for MokoSuiteOpenGraph. * * Endpoints: * GET /api/index.php/v1/mokoog/tags - List all OG tags diff --git a/source/script.php b/source/script.php index 315a788..479c916 100644 --- a/source/script.php +++ b/source/script.php @@ -1,7 +1,7 @@ <?php /** - * @package MokoJoomOpenGraph + * @package MokoSuiteOpenGraph * @author Moko Consulting <hello@mokoconsulting.tech> * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. * @license GNU General Public License version 3 or later; see LICENSE @@ -15,8 +15,8 @@ use Joomla\CMS\Language\Text; class Pkg_MokoOGInstallerScript { - protected $minimumJoomla = '4.0.0'; - protected $minimumPhp = '8.1.0'; + protected $minimumJoomla = '6.0.0'; + protected $minimumPhp = '8.2.0'; @@ -33,6 +33,16 @@ class Pkg_MokoOGInstallerScript return false; } + if (version_compare(JVERSION, $this->minimumJoomla, '<')) + { + Factory::getApplication()->enqueueMessage( + Text::sprintf('PKG_MOKOOG_JOOMLA_VERSION_ERROR', $this->minimumJoomla), + 'error' + ); + + return false; + } + $this->saveDownloadKey(); return true; @@ -128,7 +138,7 @@ class Pkg_MokoOGInstallerScript $db->getQuery(true) ->select([$db->quoteName('update_site_id'), $db->quoteName('extra_query')]) ->from($db->quoteName('#__update_sites')) - ->where('(' . $db->quoteName('name') . ' LIKE ' . $db->quote('%MokoJoomOpenGraph%') . ' OR ' . $db->quoteName('location') . ' LIKE ' . $db->quote('%MokoJoomOpenGraph%') . ')') + ->where('(' . $db->quoteName('name') . ' LIKE ' . $db->quote('%MokoSuiteOpenGraph%') . ' OR ' . $db->quoteName('location') . ' LIKE ' . $db->quote('%MokoSuiteOpenGraph%') . ')') ->setLimit(1) ); $site = $db->loadObject(); From ff2c1a0483aa76dd903365e6be07f449b7f63d8d Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" <gitea-actions[bot]@mokoconsulting.tech> Date: Sun, 28 Jun 2026 19:33:59 +0000 Subject: [PATCH 40/41] chore(version): auto-bump patch 01.04.16-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 2 +- CODE_OF_CONDUCT.md | 2 +- GOVERNANCE.md | 2 +- README.md | 2 +- SECURITY.md | 2 +- source/packages/com_mokoog/mokoog.xml | 2 +- source/packages/com_mokoog/sql/updates/mysql/01.04.16.sql | 1 + source/packages/plg_content_mokoog/mokoog.xml | 2 +- source/packages/plg_system_mokoog/mokoog.xml | 2 +- source/packages/plg_webservices_mokoog/mokoog.xml | 2 +- source/pkg_mokoog.xml | 2 +- 12 files changed, 12 insertions(+), 11 deletions(-) create mode 100644 source/packages/com_mokoog/sql/updates/mysql/01.04.16.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index ec5a2df..12e7f56 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.04.15 +# VERSION: 01.04.16 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index f1d1603..52f79f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -<!-- VERSION: 01.04.15 --> +<!-- VERSION: 01.04.16 --> All notable changes to MokoSuiteOpenGraph will be documented in this file. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 5f34561..224257c 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -14,7 +14,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://github.com/mokoconsulting-tech/Template-Joomla/ - VERSION: 01.04.15 + VERSION: 01.04.16 PATH: ./CODE_OF_CONDUCT.md BRIEF: Community expectations and enforcement guidelines NOTE: Adapted with attribution from the Contributor Covenant v2.1 diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 6152d98..2210de9 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -19,7 +19,7 @@ DEFGROUP: mokoconsulting-tech.Template-Joomla INGROUP: MokoStandards.Governance REPO: https://github.com/mokoconsulting-tech/Template-Joomla - VERSION: 01.04.15 + VERSION: 01.04.16 PATH: /GOVERNANCE.md BRIEF: Project governance rules, roles, and decision process for Template-Joomla --> diff --git a/README.md b/README.md index daf29c4..b1697f2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoSuiteOpenGraph -<!-- VERSION: 01.04.15 --> +<!-- VERSION: 01.04.16 --> Open Graph, Twitter Card, and social sharing meta tag management for Joomla 6 and higher. diff --git a/SECURITY.md b/SECURITY.md index cb1981f..befa464 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 01.04.15 +VERSION: 01.04.16 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/com_mokoog/mokoog.xml b/source/packages/com_mokoog/mokoog.xml index c0c7106..442ba1a 100644 --- a/source/packages/com_mokoog/mokoog.xml +++ b/source/packages/com_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="component" method="upgrade"> <name>com_mokoog</name> - <version>01.04.15</version> + <version>01.04.16</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/com_mokoog/sql/updates/mysql/01.04.16.sql b/source/packages/com_mokoog/sql/updates/mysql/01.04.16.sql new file mode 100644 index 0000000..cc731eb --- /dev/null +++ b/source/packages/com_mokoog/sql/updates/mysql/01.04.16.sql @@ -0,0 +1 @@ +/* 01.04.16 — no schema changes */ diff --git a/source/packages/plg_content_mokoog/mokoog.xml b/source/packages/plg_content_mokoog/mokoog.xml index 8c2a876..cdfc0ca 100644 --- a/source/packages/plg_content_mokoog/mokoog.xml +++ b/source/packages/plg_content_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="content" method="upgrade"> <name>Content - MokoSuiteOpenGraph</name> - <version>01.04.15</version> + <version>01.04.16</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_system_mokoog/mokoog.xml b/source/packages/plg_system_mokoog/mokoog.xml index 7479263..5749f04 100644 --- a/source/packages/plg_system_mokoog/mokoog.xml +++ b/source/packages/plg_system_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="system" method="upgrade"> <name>System - MokoSuiteOpenGraph</name> - <version>01.04.15</version> + <version>01.04.16</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_webservices_mokoog/mokoog.xml b/source/packages/plg_webservices_mokoog/mokoog.xml index bfbc862..592dd49 100644 --- a/source/packages/plg_webservices_mokoog/mokoog.xml +++ b/source/packages/plg_webservices_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="webservices" method="upgrade"> <name>Web Services - MokoSuiteOpenGraph</name> - <version>01.04.15</version> + <version>01.04.16</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/pkg_mokoog.xml b/source/pkg_mokoog.xml index 198fb90..e499f78 100644 --- a/source/pkg_mokoog.xml +++ b/source/pkg_mokoog.xml @@ -8,7 +8,7 @@ <extension type="package" method="upgrade"> <name>Package - MokoSuiteOpenGraph</name> <packagename>mokoog</packagename> - <version>01.04.15</version> + <version>01.04.16</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> From 77cf557b71b5956e532da911370a9085415e7b05 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" <gitea-actions[bot]@mokoconsulting.tech> Date: Sun, 28 Jun 2026 19:34:12 +0000 Subject: [PATCH 41/41] chore(version): pre-release bump to 01.04.17-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 2 +- CODE_OF_CONDUCT.md | 2 +- GOVERNANCE.md | 2 +- README.md | 2 +- SECURITY.md | 2 +- source/packages/com_mokoog/mokoog.xml | 2 +- source/packages/com_mokoog/sql/updates/mysql/01.04.17.sql | 1 + source/packages/plg_content_mokoog/mokoog.xml | 2 +- source/packages/plg_system_mokoog/mokoog.xml | 2 +- source/packages/plg_webservices_mokoog/mokoog.xml | 2 +- source/pkg_mokoog.xml | 2 +- 12 files changed, 12 insertions(+), 11 deletions(-) create mode 100644 source/packages/com_mokoog/sql/updates/mysql/01.04.17.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 12e7f56..64970a3 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.04.16 +# VERSION: 01.04.17 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index 52f79f3..9a5154a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -<!-- VERSION: 01.04.16 --> +<!-- VERSION: 01.04.17 --> All notable changes to MokoSuiteOpenGraph will be documented in this file. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 224257c..387a451 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -14,7 +14,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://github.com/mokoconsulting-tech/Template-Joomla/ - VERSION: 01.04.16 + VERSION: 01.04.17 PATH: ./CODE_OF_CONDUCT.md BRIEF: Community expectations and enforcement guidelines NOTE: Adapted with attribution from the Contributor Covenant v2.1 diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 2210de9..a072363 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -19,7 +19,7 @@ DEFGROUP: mokoconsulting-tech.Template-Joomla INGROUP: MokoStandards.Governance REPO: https://github.com/mokoconsulting-tech/Template-Joomla - VERSION: 01.04.16 + VERSION: 01.04.17 PATH: /GOVERNANCE.md BRIEF: Project governance rules, roles, and decision process for Template-Joomla --> diff --git a/README.md b/README.md index b1697f2..1957163 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoSuiteOpenGraph -<!-- VERSION: 01.04.16 --> +<!-- VERSION: 01.04.17 --> Open Graph, Twitter Card, and social sharing meta tag management for Joomla 6 and higher. diff --git a/SECURITY.md b/SECURITY.md index befa464..694dd16 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 01.04.16 +VERSION: 01.04.17 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/com_mokoog/mokoog.xml b/source/packages/com_mokoog/mokoog.xml index 442ba1a..7c6e135 100644 --- a/source/packages/com_mokoog/mokoog.xml +++ b/source/packages/com_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="component" method="upgrade"> <name>com_mokoog</name> - <version>01.04.16</version> + <version>01.04.17</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/com_mokoog/sql/updates/mysql/01.04.17.sql b/source/packages/com_mokoog/sql/updates/mysql/01.04.17.sql new file mode 100644 index 0000000..27e15a2 --- /dev/null +++ b/source/packages/com_mokoog/sql/updates/mysql/01.04.17.sql @@ -0,0 +1 @@ +/* 01.04.17 — no schema changes */ diff --git a/source/packages/plg_content_mokoog/mokoog.xml b/source/packages/plg_content_mokoog/mokoog.xml index cdfc0ca..5843452 100644 --- a/source/packages/plg_content_mokoog/mokoog.xml +++ b/source/packages/plg_content_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="content" method="upgrade"> <name>Content - MokoSuiteOpenGraph</name> - <version>01.04.16</version> + <version>01.04.17</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_system_mokoog/mokoog.xml b/source/packages/plg_system_mokoog/mokoog.xml index 5749f04..4ce91d7 100644 --- a/source/packages/plg_system_mokoog/mokoog.xml +++ b/source/packages/plg_system_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="system" method="upgrade"> <name>System - MokoSuiteOpenGraph</name> - <version>01.04.16</version> + <version>01.04.17</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/packages/plg_webservices_mokoog/mokoog.xml b/source/packages/plg_webservices_mokoog/mokoog.xml index 592dd49..9d907e5 100644 --- a/source/packages/plg_webservices_mokoog/mokoog.xml +++ b/source/packages/plg_webservices_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> <extension type="plugin" group="webservices" method="upgrade"> <name>Web Services - MokoSuiteOpenGraph</name> - <version>01.04.16</version> + <version>01.04.17</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail> diff --git a/source/pkg_mokoog.xml b/source/pkg_mokoog.xml index e499f78..93f82b3 100644 --- a/source/pkg_mokoog.xml +++ b/source/pkg_mokoog.xml @@ -8,7 +8,7 @@ <extension type="package" method="upgrade"> <name>Package - MokoSuiteOpenGraph</name> <packagename>mokoog</packagename> - <version>01.04.16</version> + <version>01.04.17</version> <creationDate>2026-05-23</creationDate> <author>Moko Consulting</author> <authorEmail>hello@mokoconsulting.tech</authorEmail>