diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml
index 1dcd58b..8891b59 100644
--- a/.mokogitea/manifest.xml
+++ b/.mokogitea/manifest.xml
@@ -9,7 +9,7 @@
Template - MokoOnyx
MokoConsulting
MokoOnyx - Joomla site template (successor to MokoCassiopeia)
- 02.19.00
+ 02.19.02
GNU General Public License v3
diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml
index 6958f88..f450d94 100644
--- a/.mokogitea/workflows/issue-branch.yml
+++ b/.mokogitea/workflows/issue-branch.yml
@@ -5,7 +5,7 @@
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Automation
-# VERSION: 02.19.00
+# VERSION: 02.19.02
# BRIEF: Auto-create feature branch when an issue is opened
name: "Universal: Issue Branch"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2c73908..6226b5b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,11 +8,11 @@
DEFGROUP: Joomla.Template.Site
INGROUP: MokoOnyx.Documentation
PATH: ./CHANGELOG.md
- VERSION: 02.19.00
+ VERSION: 02.19.02
BRIEF: Changelog file documenting version history of MokoOnyx
-->
-# Changelog — MokoOnyx (VERSION: 02.19.00)
+# Changelog — MokoOnyx (VERSION: 02.19.02)
## [Unreleased]
## [02.19.00] --- 2026-06-04
diff --git a/SECURITY.md b/SECURITY.md
index 28608f9..d707c57 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -10,7 +10,7 @@
INGROUP: MokoOnyx.Governance
REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx
FILE: SECURITY.md
- VERSION: 02.19.00
+ VERSION: 02.19.02
BRIEF: Security policy and vulnerability reporting process for MokoOnyx.
PATH: /SECURITY.md
NOTE: This policy is process oriented and does not replace secure engineering practices.
diff --git a/src/html/layouts/joomla/module/card.php b/src/html/layouts/joomla/module/card.php
index 42e39a4..807b96b 100644
--- a/src/html/layouts/joomla/module/card.php
+++ b/src/html/layouts/joomla/module/card.php
@@ -10,7 +10,7 @@
* INGROUP: MokoOnyx
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx
* PATH: /html/layouts/joomla/module/card.php
- * VERSION: 02.19.00
+ * VERSION: 02.19.02
* BRIEF: Custom card module chrome — renders module titles for all modules
*/
diff --git a/src/html/layouts/mokoonyx/article-metadata.php b/src/html/layouts/mokoonyx/article-metadata.php
index ce119e9..2e21eee 100644
--- a/src/html/layouts/mokoonyx/article-metadata.php
+++ b/src/html/layouts/mokoonyx/article-metadata.php
@@ -11,7 +11,7 @@
* INGROUP: MokoOnyx.Layouts
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx
* PATH: /src/html/layouts/mokoonyx/article-metadata.php
- * VERSION: 02.19.00
+ * VERSION: 02.19.02
* BRIEF: Article metadata footer layout -- renders jcfields grouped by field group
*/
diff --git a/src/media/css/a11y-high-contrast.css b/src/media/css/a11y-high-contrast.css
index 3dd2da1..a7a5503 100644
--- a/src/media/css/a11y-high-contrast.css
+++ b/src/media/css/a11y-high-contrast.css
@@ -10,7 +10,7 @@
* INGROUP: MokoOnyx.Accessibility
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx
* PATH: ./media/css/a11y-high-contrast.css
- * VERSION: 02.19.00
+ * VERSION: 02.19.02
* BRIEF: High-contrast stylesheet for accessibility toolbar
*/
diff --git a/src/script.php b/src/script.php
index c48027e..35f6641 100644
--- a/src/script.php
+++ b/src/script.php
@@ -94,6 +94,7 @@ class Tpl_MokoonyxInstallerScript implements InstallerScriptInterface
$this->clearFaviconStamp();
$this->cleanMediaFolder();
$this->removeDeletedFiles();
+ $this->removeDuplicateExtensions();
$this->lockExtension();
}
@@ -484,6 +485,96 @@ class Tpl_MokoonyxInstallerScript implements InstallerScriptInterface
}
}
+ /**
+ * Remove duplicate MokoOnyx extension entries from #__extensions.
+ *
+ * Re-installs or migrations can leave ghost rows. We keep the one
+ * that is locked (the active template) and delete any extras.
+ * Also removes stale MokoCassiopeia entries if present.
+ */
+ private function removeDuplicateExtensions(): void
+ {
+ $db = Factory::getDbo();
+
+ // Find all MokoOnyx template entries
+ $rows = $db->setQuery(
+ $db->getQuery(true)
+ ->select(['extension_id', 'locked'])
+ ->from('#__extensions')
+ ->where($db->quoteName('element') . ' = ' . $db->quote(self::NEW_NAME))
+ ->where($db->quoteName('type') . ' = ' . $db->quote('template'))
+ ->order('locked DESC, extension_id ASC')
+ )->loadObjectList();
+
+ if (count($rows) > 1) {
+ $keep = (int) $rows[0]->extension_id;
+ $removed = 0;
+
+ for ($i = 1; $i < count($rows); $i++) {
+ $staleId = (int) $rows[$i]->extension_id;
+
+ $db->setQuery(
+ $db->getQuery(true)
+ ->delete('#__update_sites_extensions')
+ ->where('extension_id = ' . $staleId)
+ )->execute();
+
+ $db->setQuery(
+ $db->getQuery(true)
+ ->delete('#__extensions')
+ ->where('extension_id = ' . $staleId)
+ )->execute();
+
+ $removed++;
+ }
+
+ if ($removed > 0) {
+ $this->logMessage("Removed {$removed} duplicate MokoOnyx extension(s). Kept ID {$keep}.");
+ }
+ }
+
+ // Remove stale MokoCassiopeia if not set as default
+ $oldExt = (int) $db->setQuery(
+ $db->getQuery(true)
+ ->select('extension_id')
+ ->from('#__extensions')
+ ->where($db->quoteName('element') . ' = ' . $db->quote(self::OLD_NAME))
+ ->where($db->quoteName('type') . ' = ' . $db->quote('template'))
+ )->loadResult();
+
+ if ($oldExt) {
+ $isDefault = (int) $db->setQuery(
+ $db->getQuery(true)
+ ->select('COUNT(*)')
+ ->from('#__template_styles')
+ ->where($db->quoteName('template') . ' = ' . $db->quote(self::OLD_NAME))
+ ->where($db->quoteName('home') . ' = 1')
+ )->loadResult();
+
+ if ($isDefault === 0) {
+ $db->setQuery(
+ $db->getQuery(true)
+ ->delete('#__update_sites_extensions')
+ ->where('extension_id = ' . $oldExt)
+ )->execute();
+
+ $db->setQuery(
+ $db->getQuery(true)
+ ->delete('#__extensions')
+ ->where('extension_id = ' . $oldExt)
+ )->execute();
+
+ $db->setQuery(
+ $db->getQuery(true)
+ ->delete('#__template_styles')
+ ->where($db->quoteName('template') . ' = ' . $db->quote(self::OLD_NAME))
+ )->execute();
+
+ $this->logMessage('Removed stale MokoCassiopeia extension and styles.');
+ }
+ }
+ }
+
/**
* Remove files and directories that were shipped in previous versions
* but have since been deleted from the package.
diff --git a/src/templateDetails.xml b/src/templateDetails.xml
index e905250..02d4690 100644
--- a/src/templateDetails.xml
+++ b/src/templateDetails.xml
@@ -32,11 +32,11 @@
- https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/raw/branch/main/updates.xml
+ https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/updates.xml
mokoonyx
- 02.19.00
+ 02.19.02-dev
script.php
2026-05-16
Jonathan Miller || Moko Consulting
diff --git a/updates.xml b/updates.xml
index f7ef071..8d493ff 100644
--- a/updates.xml
+++ b/updates.xml
@@ -1,7 +1,7 @@
@@ -11,13 +11,13 @@
mokoonyx
template
site
- 02.18.04-dev
+ 02.19.02-dev
2026-06-04
https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/development
- https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/development/tpl_mokoonyx-02.18.04-dev.zip
+ https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/development/tpl_mokoonyx-02.19.02-dev.zip
- 32e888ba865ab28a96afe1a1f76380c4a3b341389f730e9c6899bdcac9b2bd36
+ 5811cc175564744865053f5a2cf259e66016c4e41f2d87571d19b1dd5f7fd8ba
dev
https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/raw/branch/main/CHANGELOG.md
Moko Consulting