refactor: restructure from package back to standalone module #63

Merged
jmiller merged 5 commits from dev into main 2026-06-04 16:31:11 +00:00
39 changed files with 21 additions and 344 deletions
+9 -17
View File
@@ -32,26 +32,21 @@ composer install # Install PHP dependencies
## Architecture
This is a Joomla **package** extension (`pkg_mokojoomhero`) containing two sub-extensions:
This is a Joomla **site module** (`mod_mokojoomhero`).
### mod_mokojoomhero (Site Module)
- Random hero image slideshow or background video with content overlay
- Supports image folders, YouTube, Vimeo, local video, solid colour, gradient
- Configurable overlay, text alignment, card animation, parallax, content animations
- Works independently — no plugin dependency required
### plg_system_mokojoomhero (System Plugin)
- License check — free tier (no key required), pro tier warns if download key missing
- Controlled by `LICENSE_TYPE` constant ('free' or 'pro') in the Extension class
- Auto-enabled on package install via `pkg_script.php`
- Namespace: `Joomla\Plugin\System\MokoJoomHero`
- A/B testing with weighted variations, scheduling with start/end datetime
- Article content source, per-slide unique content via subform
### Key files
- `src/pkg_mokojoomhero.xml`package manifest
- `src/pkg_script.php` — auto-enables system plugin on install
- `src/packages/mod_mokojoomhero/` — hero module source
- `src/packages/plg_system_mokojoomhero/` — system plugin source
- `updates.xml` — Joomla update server (includes legacy module entries for migration)
- `src/mod_mokojoomhero.xml`module manifest
- `src/mod_mokojoomhero.php` — module entry point
- `src/tmpl/default.php` — template
- `src/media/` — CSS, JS, and web asset registry
- `src/language/` — en-GB and en-US translations
- `src/script.php` — install script (creates default image folder)
## Rules
@@ -66,7 +61,4 @@ This is a Joomla **package** extension (`pkg_mokojoomhero`) containing two sub-e
## Coding Standards
- PHP 8.1+ minimum
- Joomla 5/6 DI container pattern: `services/provider.php` → Extension class
- Legacy stub `.php` file required for plugin loader but empty
- `SubscriberInterface` for event subscription (not `on*` method naming)
- SPDX license headers on all PHP files
+6 -24
View File
@@ -13,7 +13,7 @@
# Extension Configuration
EXTENSION_NAME := mokojoomhero
EXTENSION_TYPE := package
EXTENSION_TYPE := module
# Options: module, plugin, component, package, template
EXTENSION_VERSION := 1.0.0
@@ -156,29 +156,11 @@ clean: ## Clean build artifacts
.PHONY: build
build: clean ## Build extension package
@echo "$(COLOR_BLUE)Building Joomla package extension...$(COLOR_RESET)"
@mkdir -p $(DIST_DIR) $(BUILD_DIR)/packages
@# --- Build each sub-extension as a separate ZIP ---
@for EXT_DIR in $(SRC_DIR)/packages/*/; do \
EXT_NAME=$$(basename "$$EXT_DIR"); \
[ "$$EXT_NAME" = "index.html" ] && continue; \
echo " Packaging $$EXT_NAME..."; \
cd "$$EXT_DIR" && $(ZIP) -r "$(CURDIR)/$(BUILD_DIR)/packages/$${EXT_NAME}.zip" . \
-x "*.git*" -x "*/index.html" 2>/dev/null; \
cd "$(CURDIR)"; \
done
@# --- Build the outer package ZIP ---
@echo " Assembling pkg_$(EXTENSION_NAME)..."
@cp $(SRC_DIR)/pkg_mokojoomhero.xml $(BUILD_DIR)/pkg_mokojoomhero.xml
@cp $(SRC_DIR)/pkg_script.php $(BUILD_DIR)/pkg_script.php
@cd $(BUILD_DIR) && $(ZIP) -r "$(CURDIR)/$(DIST_DIR)/pkg_$(EXTENSION_NAME)-$(EXTENSION_VERSION).zip" \
pkg_mokojoomhero.xml pkg_script.php packages/
@echo "$(COLOR_GREEN)✓ Package created: $(DIST_DIR)/pkg_$(EXTENSION_NAME)-$(EXTENSION_VERSION).zip$(COLOR_RESET)"
@echo " Contents:"
@unzip -l "$(DIST_DIR)/pkg_$(EXTENSION_NAME)-$(EXTENSION_VERSION).zip" | tail -n +4 | head -20
@echo "$(COLOR_BLUE)Building Joomla module extension...$(COLOR_RESET)"
@mkdir -p $(DIST_DIR)
@cd $(SRC_DIR) && $(ZIP) -r "$(CURDIR)/$(DIST_DIR)/mod_$(EXTENSION_NAME)-$(EXTENSION_VERSION).zip" . \
-x "*.git*" -x "*/index.html" 2>/dev/null
@echo "$(COLOR_GREEN)✓ Module created: $(DIST_DIR)/mod_$(EXTENSION_NAME)-$(EXTENSION_VERSION).zip$(COLOR_RESET)"
.PHONY: package
package: build ## Alias for build
@@ -6,8 +6,8 @@
* DEFGROUP: MokoJoomHero.Module.Assets
* INGROUP: MokoJoomHero.Module
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero
* PATH: /src/packages/mod_mokojoomhero/media/css/mod_mokojoomhero.css
* VERSION: 01.12.00
* PATH: /src/media/css/mod_mokojoomhero.css
* VERSION: 01.10.00
* BRIEF: Hero module stylesheet slideshow, video, colour/gradient, overlay, card, mute toggle, responsive
*/
@@ -7,8 +7,8 @@
* DEFGROUP: MokoJoomHero.Module.Assets
* INGROUP: MokoJoomHero.Module
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero
* PATH: /src/packages/mod_mokojoomhero/media/js/mod_mokojoomhero.js
* VERSION: 01.12.00
* PATH: /src/media/js/mod_mokojoomhero.js
* VERSION: 01.10.00
* BRIEF: Hero module JavaScript slideshow crossfade, video viewport control, mute toggle
*/
@@ -10,13 +10,13 @@
DEFGROUP: MokoJoomHero.Module
INGROUP: MokoJoomHero
REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero
PATH: /src/packages/mod_mokojoomhero/mod_mokojoomhero.xml
PATH: /src/mod_mokojoomhero.xml
VERSION: 01.00.20
BRIEF: Joomla module manifest — random hero image with content overlay
-->
<extension type="module" client="site" method="upgrade">
<name>Module - MokoJoomHero</name>
<creationDate>2026-05</creationDate>
<creationDate>2026-06-04</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
@@ -1 +0,0 @@
<html><body bgcolor="#FFFFFF"></body></html>
@@ -1 +0,0 @@
<html><body bgcolor="#FFFFFF"></body></html>
@@ -1 +0,0 @@
<html><body bgcolor="#FFFFFF"></body></html>
@@ -1 +0,0 @@
<html><body bgcolor="#FFFFFF"></body></html>
@@ -1,2 +0,0 @@
; Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
; SPDX-License-Identifier: GPL-3.0-or-later
@@ -1,5 +0,0 @@
; Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
; SPDX-License-Identifier: GPL-3.0-or-later
PLG_SYSTEM_MOKOJOOMHERO="System - MokoJoomHero"
PLG_SYSTEM_MOKOJOOMHERO_DESCRIPTION="System plugin for MokoJoomHero — license and update management"
@@ -1 +0,0 @@
<html><body bgcolor="#FFFFFF"></body></html>
@@ -1,2 +0,0 @@
; Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
; SPDX-License-Identifier: GPL-3.0-or-later
@@ -1,5 +0,0 @@
; Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
; SPDX-License-Identifier: GPL-3.0-or-later
PLG_SYSTEM_MOKOJOOMHERO="System - MokoJoomHero"
PLG_SYSTEM_MOKOJOOMHERO_DESCRIPTION="System plugin for MokoJoomHero — license and update management"
@@ -1 +0,0 @@
<html><body bgcolor="#FFFFFF"></body></html>
@@ -1,12 +0,0 @@
<?php
/**
* @package MokoJoomHero
* @subpackage plg_system_mokojoomhero
* @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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
defined('_JEXEC') or die;
@@ -1,34 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
* @package MokoJoomHero
* @subpackage plg_system_mokojoomhero
* @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>PLG_SYSTEM_MOKOJOOMHERO</name>
<version>01.12.00</version>
<creationDate>2026-06-02</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<copyright>Copyright (C) 2026 Moko Consulting. All rights reserved.</copyright>
<license>GPL-3.0-or-later</license>
<description>PLG_SYSTEM_MOKOJOOMHERO_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\System\MokoJoomHero</namespace>
<files>
<filename plugin="mokojoomhero">mokojoomhero.php</filename>
<folder>services</folder>
<folder>src</folder>
</files>
<languages folder="language">
<language tag="en-GB">en-GB/plg_system_mokojoomhero.ini</language>
<language tag="en-GB">en-GB/plg_system_mokojoomhero.sys.ini</language>
<language tag="en-US">en-US/plg_system_mokojoomhero.ini</language>
<language tag="en-US">en-US/plg_system_mokojoomhero.sys.ini</language>
</languages>
</extension>
@@ -1 +0,0 @@
<html><body bgcolor="#FFFFFF"></body></html>
@@ -1,39 +0,0 @@
<?php
/**
* @package MokoJoomHero
* @subpackage plg_system_mokojoomhero
* @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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\System\MokoJoomHero\Extension\MokoJoomHero;
return new class implements ServiceProviderInterface {
public function register(Container $container): void
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new MokoJoomHero(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('system', 'mokojoomhero')
);
$plugin->setApplication(Factory::getApplication());
return $plugin;
}
);
}
};
@@ -1,100 +0,0 @@
<?php
/**
* @package MokoJoomHero
* @subpackage plg_system_mokojoomhero
* @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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
namespace Joomla\Plugin\System\MokoJoomHero\Extension;
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\SubscriberInterface;
class MokoJoomHero extends CMSPlugin implements SubscriberInterface
{
/**
* License type: 'free' requires no key, 'pro' requires a valid download key.
*/
private const LICENSE_TYPE = 'free';
public static function getSubscribedEvents(): array
{
return [
'onAfterRoute' => 'onAfterRoute',
];
}
public function onAfterRoute(): void
{
$app = $this->getApplication();
if ($app->isClient('administrator')) {
$this->checkLicense();
}
}
/**
* Check license status once per session. Free tier requires no key.
* Pro tier warns if no valid download key is configured.
*/
private function checkLicense(): void
{
if (self::LICENSE_TYPE === 'free') {
return;
}
$session = Factory::getSession();
if ($session->get('mokojoomhero.license_checked', false)) {
return;
}
$user = Factory::getUser();
if ($user->guest || !$user->authorise('core.manage')) {
return;
}
$session->set('mokojoomhero.license_checked', true);
try {
$db = Factory::getDbo();
$query = $db->getQuery(true)
->select($db->quoteName('extra_query'))
->from($db->quoteName('#__update_sites'))
->where($db->quoteName('name') . ' = ' . $db->quote('MokoJoomHero Updates'))
->setLimit(1);
$db->setQuery($query);
$extraQuery = (string) $db->loadResult();
if (!empty($extraQuery)) {
parse_str($extraQuery, $parsed);
if (!empty($parsed['dlid']) && preg_match('/^MOKO-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/', $parsed['dlid'])) {
return;
}
}
$this->getApplication()->enqueueMessage(
'<strong>MokoJoomHero — Download Key Required</strong> — '
. 'No download key is configured. Updates may not be available until a valid key is entered. '
. 'Go to <a href="index.php?option=com_installer&view=updatesites">System &rarr; Update Sites</a> '
. 'and enter your download key (<code>MOKO-XXXX-XXXX-XXXX-XXXX</code>) for the MokoJoomHero update site.',
'warning'
);
} catch (\RuntimeException $e) {
$this->getApplication()->getLogger()->warning(
'MokoJoomHero license check failed: ' . $e->getMessage(),
['exception' => $e]
);
}
}
}
@@ -1 +0,0 @@
<html><body bgcolor="#FFFFFF"></body></html>
@@ -1 +0,0 @@
<html><body bgcolor="#FFFFFF"></body></html>
-30
View File
@@ -1,30 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
* @package MokoJoomHero
* @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="package" method="upgrade">
<name>Package - MokoJoomHero</name>
<packagename>mokojoomhero</packagename>
<version>01.12.00</version>
<creationDate>2026-06-02</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<copyright>Copyright (C) 2026 Moko Consulting. All rights reserved.</copyright>
<license>GPL-3.0-or-later</license>
<description>Random hero image slideshow or background video with content overlay. Includes the hero module and system plugin. By Moko Consulting.</description>
<scriptfile>pkg_script.php</scriptfile>
<files folder="packages">
<file type="module" id="mod_mokojoomhero" client="site">mod_mokojoomhero.zip</file>
<file type="plugin" id="mokojoomhero" group="system">plg_system_mokojoomhero.zip</file>
</files>
<updateservers>
<server type="extension" name="MokoJoomHero Updates">https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero/updates.xml</server>
</updateservers>
</extension>
-59
View File
@@ -1,59 +0,0 @@
<?php
/**
* @package MokoJoomHero
* @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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Installer\InstallerAdapter;
class Pkg_MokoJoomHeroInstallerScript
{
/**
* Called after install/update — only enables the system plugin on fresh install.
*
* @param string $type Action type
* @param InstallerAdapter $parent Installer adapter
*
* @return void
*/
public function postflight(string $type, InstallerAdapter $parent): void
{
if ($type === 'install') {
try {
$db = Factory::getDbo();
// Enable the system plugin automatically on fresh install
$query = $db->getQuery(true)
->update($db->quoteName('#__extensions'))
->set($db->quoteName('enabled') . ' = 1')
->where($db->quoteName('type') . ' = ' . $db->quote('plugin'))
->where($db->quoteName('folder') . ' = ' . $db->quote('system'))
->where($db->quoteName('element') . ' = ' . $db->quote('mokojoomhero'));
$db->setQuery($query);
$db->execute();
if ($db->getAffectedRows() === 0) {
Factory::getApplication()->enqueueMessage(
'MokoJoomHero: The system plugin could not be auto-enabled. '
. 'Please enable it manually in Extensions &rarr; Plugins.',
'warning'
);
}
} catch (\Exception $e) {
Factory::getApplication()->enqueueMessage(
'MokoJoomHero: Failed to auto-enable system plugin: ' . $e->getMessage()
. ' — Please enable it manually in Extensions &rarr; Plugins.',
'warning'
);
}
}
}
}