From a6a24f72de04f3a54eeec3a1a81d8226c7495610 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sun, 5 Jul 2026 22:54:38 -0500 Subject: [PATCH 1/2] fix(installer): gate install-success on real verification Joomla installers keep running after a failed sub-step, so a postflight "installed successfully" / next-steps message can lie. Gate the success path in both installer scripts. Package (source/script.php): postflight now verifies every child extension declared in the package manifest actually registered in #__extensions (element + type, plus folder for plugins). If any are missing it enqueues an error listing them and returns before the licence notice. Download-key restore and plugin-enable housekeeping still run unconditionally. Check fails open on any error. Component (source/packages/com_mokoog/script.php): install() now parses the installed SQL (JPATH_ADMINISTRATOR/components/com_mokoog/sql/install.mysql.sql) for CREATE TABLE declarations and verifies each exists (prefix-substituted, case-insensitive) before echoing the success message. Fails open if the SQL is unreadable. Claude-Session: https://claude.ai/code/session_01MrxXW9iyuDda5yDZrjZMbF --- source/packages/com_mokoog/script.php | 62 ++++++++++++++++++ source/script.php | 94 ++++++++++++++++++++++++++- 2 files changed, 155 insertions(+), 1 deletion(-) diff --git a/source/packages/com_mokoog/script.php b/source/packages/com_mokoog/script.php index ace5636..c77018c 100644 --- a/source/packages/com_mokoog/script.php +++ b/source/packages/com_mokoog/script.php @@ -10,6 +10,7 @@ defined('_JEXEC') or die; +use Joomla\CMS\Factory; use Joomla\CMS\Installer\InstallerAdapter; class Com_MokoOGInstallerScript @@ -23,6 +24,25 @@ class Com_MokoOGInstallerScript */ public function install(InstallerAdapter $parent): void { + // Be smart: verify EVERY table the component's own install SQL declares is + // actually present. If any are missing the install did not complete — say + // so instead of showing "installed successfully". + $missing = $this->missingTables(); + + if ($missing !== []) { + $detail = count($missing) > 6 + ? count($missing) . ' expected database tables are missing' + : 'missing tables: ' . htmlspecialchars(implode(', ', $missing), ENT_QUOTES, 'UTF-8'); + + Factory::getApplication()->enqueueMessage( + 'MokoSuiteOpenGraph did not install correctly. ' . $detail + . ' — review the errors above, then reinstall.', + 'error' + ); + + return; + } + echo '

MokoSuiteOpenGraph component installed successfully.

'; } @@ -37,4 +57,46 @@ class Com_MokoOGInstallerScript { echo '

MokoSuiteOpenGraph component updated successfully.

'; } + + /** + * Smart schema check: parse the component's own installed install SQL for every + * CREATE TABLE, then return the ones missing from the database. An empty array + * means the full schema installed. Fails open (returns []) if the SQL can't be + * read, so a good install is never flagged as failed. + * + * @return string[] Names (without the #__ prefix) of missing tables. + */ + private function missingTables(): array + { + try { + $sqlFile = JPATH_ADMINISTRATOR . '/components/com_mokoog/sql/install.mysql.sql'; + + if (!is_file($sqlFile)) { + return []; + } + + $sql = file_get_contents($sqlFile); + + if ($sql === false + || !preg_match_all('/CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?[`"\']?(#__[A-Za-z0-9_]+)/i', $sql, $m)) { + return []; + } + + $db = Factory::getDbo(); + $prefix = strtolower($db->getPrefix()); + $have = array_map('strtolower', $db->getTableList()); + $missing = []; + + foreach (array_unique($m[1]) as $decl) { + $bare = substr($decl, 3); // strip "#__" + if (!in_array($prefix . strtolower($bare), $have, true)) { + $missing[] = $bare; + } + } + + return $missing; + } catch (\Throwable $e) { + return []; + } + } } diff --git a/source/script.php b/source/script.php index c90d65e..5ab47da 100644 --- a/source/script.php +++ b/source/script.php @@ -50,9 +50,10 @@ class Pkg_MokoOGInstallerScript public function postflight(string $type, InstallerAdapter $parent): void { + // The download key must be restored regardless of the install outcome. $this->restoreDownloadKey(); - $this->warnMissingLicenseKey(); + // Housekeeping: ensure the bundled plugins are enabled after a fresh install. if ($type === 'install') { $db = Factory::getContainer()->get(\Joomla\Database\DatabaseInterface::class); @@ -69,6 +70,97 @@ class Pkg_MokoOGInstallerScript )->execute(); } } + + // Be smart: only run the success path (the licence notice, which implies a + // completed install) when every child extension declared in the package + // manifest actually registered. If any are missing, the package did NOT + // install successfully — surface that instead of implying success. + $missing = $this->missingChildExtensions($parent); + + if ($missing !== []) + { + Factory::getApplication()->enqueueMessage( + 'MokoSuiteOpenGraph did not install completely. ' + . 'The following extension(s) failed to install: ' . htmlspecialchars(implode(', ', $missing), ENT_QUOTES, 'UTF-8') . '. ' + . 'Resolve the errors shown above, then uninstall and reinstall the package.', + 'error' + ); + + return; + } + + $this->warnMissingLicenseKey(); + } + + /** + * Compare the child extensions the package manifest declares against what is + * actually registered in #__extensions after the install/update. Returns the + * label of each declared extension that is missing (i.e. failed to install); + * an empty array means every child installed and the package succeeded. + * + * Fails open: any error returns [] so a query problem can never turn a good + * install into a false "did not install completely" warning. + * + * @param InstallerAdapter $parent Installer adapter + * + * @return string[] + */ + private function missingChildExtensions(InstallerAdapter $parent): array + { + $missing = []; + + try + { + $manifest = $parent->getParent()->getManifest(); + + if (!$manifest || !isset($manifest->files) || !isset($manifest->files->file)) + { + return []; + } + + $db = Factory::getContainer()->get(\Joomla\Database\DatabaseInterface::class); + + foreach ($manifest->files->file as $file) + { + $childType = (string) $file['type']; + $element = (string) $file['id']; + $group = (string) $file['group']; + + if ($element === '') + { + continue; + } + + $query = $db->getQuery(true) + ->select('COUNT(*)') + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('element') . ' = ' . $db->quote($element)); + + if ($childType !== '') + { + $query->where($db->quoteName('type') . ' = ' . $db->quote($childType)); + } + + // Plugins are only unique by element + folder (the manifest "group"). + if ($childType === 'plugin' && $group !== '') + { + $query->where($db->quoteName('folder') . ' = ' . $db->quote($group)); + } + + $db->setQuery($query); + + if ((int) $db->loadResult() === 0) + { + $missing[] = $element . ($group !== '' ? ' (' . $group . ')' : ''); + } + } + } + catch (\Throwable $e) + { + return []; + } + + return $missing; } -- 2.52.0 From a6fe8fb5803558f0424654e99bab1f0bbebf2363 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Mon, 6 Jul 2026 03:55:02 +0000 Subject: [PATCH 2/2] chore(version): pre-release bump to 01.07.05-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.07.05.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.07.05.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index aceb8ba..f747342 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.07.03 +# VERSION: 01.07.05 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index 34c2813..ca9b8cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to MokoSuiteOpenGraph will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - + ## [Unreleased] diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index be9a704..efc28f4 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.07.03 + VERSION: 01.07.05 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 2bd9da7..8c2169b 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.07.03 + VERSION: 01.07.05 PATH: /GOVERNANCE.md BRIEF: Project governance rules, roles, and decision process for Template-Joomla --> diff --git a/README.md b/README.md index 3883664..7c7f84f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoSuiteOpenGraph - + Open Graph, Twitter Card, and social sharing meta tag management for Joomla 6 and higher. diff --git a/SECURITY.md b/SECURITY.md index 00e8f32..6b6da73 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.07.03 +VERSION: 01.07.05 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/com_mokoog/mokoog.xml b/source/packages/com_mokoog/mokoog.xml index 73f76f7..3d98fd5 100644 --- a/source/packages/com_mokoog/mokoog.xml +++ b/source/packages/com_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> com_mokoog - 01.07.03 + 01.07.05 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/com_mokoog/sql/updates/mysql/01.07.05.sql b/source/packages/com_mokoog/sql/updates/mysql/01.07.05.sql new file mode 100644 index 0000000..6c1be34 --- /dev/null +++ b/source/packages/com_mokoog/sql/updates/mysql/01.07.05.sql @@ -0,0 +1 @@ +/* 01.07.05 — no schema changes */ diff --git a/source/packages/plg_content_mokoog/mokoog.xml b/source/packages/plg_content_mokoog/mokoog.xml index 9aec28d..4dfbffb 100644 --- a/source/packages/plg_content_mokoog/mokoog.xml +++ b/source/packages/plg_content_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> Content - MokoSuiteOpenGraph - 01.07.03 + 01.07.05 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 7621fbf..0b97545 100644 --- a/source/packages/plg_system_mokoog/mokoog.xml +++ b/source/packages/plg_system_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> System - MokoSuiteOpenGraph - 01.07.03 + 01.07.05 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 3d948ce..583ae34 100644 --- a/source/packages/plg_webservices_mokoog/mokoog.xml +++ b/source/packages/plg_webservices_mokoog/mokoog.xml @@ -8,7 +8,7 @@ --> Web Services - MokoSuiteOpenGraph - 01.07.03 + 01.07.05 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokoog.xml b/source/pkg_mokoog.xml index a3037b5..74359f0 100644 --- a/source/pkg_mokoog.xml +++ b/source/pkg_mokoog.xml @@ -8,7 +8,7 @@ Package - MokoSuiteOpenGraph mokoog - 01.07.03 + 01.07.05 2026-05-23 Moko Consulting hello@mokoconsulting.tech -- 2.52.0