feat: scheduling, A/B testing, repo metadata update #60

Merged
jmiller merged 3 commits from dev into main 2026-06-04 15:03:36 +00:00
8 changed files with 236 additions and 5 deletions
@@ -90,6 +90,25 @@ MOD_MOKOJOOMHERO_PARALLAX_DESC="Background moves at a slower rate than page cont
MOD_MOKOJOOMHERO_PARALLAX_SPEED_LABEL="Parallax Speed"
MOD_MOKOJOOMHERO_PARALLAX_SPEED_DESC="How much the background moves relative to scroll (0.1 = subtle, 0.9 = dramatic)."
; A/B testing
MOD_MOKOJOOMHERO_FIELDSET_AB="A/B Testing"
MOD_MOKOJOOMHERO_AB_ENABLED_LABEL="Enable A/B Testing"
MOD_MOKOJOOMHERO_AB_ENABLED_DESC="Randomly show different content variations to visitors. Assignment is sticky per session."
MOD_MOKOJOOMHERO_AB_VARIATIONS_LABEL="Variations"
MOD_MOKOJOOMHERO_AB_VARIATIONS_DESC="Define content variations with relative weights. Higher weight = higher chance of being shown."
MOD_MOKOJOOMHERO_AB_VAR_LABEL="Label"
MOD_MOKOJOOMHERO_AB_VAR_CONTENT="Content"
MOD_MOKOJOOMHERO_AB_VAR_WEIGHT="Weight"
; Scheduling
MOD_MOKOJOOMHERO_FIELDSET_SCHEDULING="Scheduling"
MOD_MOKOJOOMHERO_SCHEDULE_ENABLED_LABEL="Enable Scheduling"
MOD_MOKOJOOMHERO_SCHEDULE_ENABLED_DESC="Only display the hero during a specific date/time range. Uses the site timezone."
MOD_MOKOJOOMHERO_SCHEDULE_START_LABEL="Start Date/Time"
MOD_MOKOJOOMHERO_SCHEDULE_START_DESC="The hero will not display before this date and time. Leave empty for no start restriction."
MOD_MOKOJOOMHERO_SCHEDULE_END_LABEL="End Date/Time"
MOD_MOKOJOOMHERO_SCHEDULE_END_DESC="The hero will not display after this date and time. Leave empty for no end restriction."
; Video poster
MOD_MOKOJOOMHERO_VIDEO_POSTER_LABEL="Video Poster Image"
MOD_MOKOJOOMHERO_VIDEO_POSTER_DESC="Fallback image displayed while the video loads. Prevents a blank hero on slow connections."
@@ -91,6 +91,25 @@ MOD_MOKOJOOMHERO_PARALLAX_DESC="Background moves at a slower rate than page cont
MOD_MOKOJOOMHERO_PARALLAX_SPEED_LABEL="Parallax Speed"
MOD_MOKOJOOMHERO_PARALLAX_SPEED_DESC="How much the background moves relative to scroll (0.1 = subtle, 0.9 = dramatic)."
; A/B testing
MOD_MOKOJOOMHERO_FIELDSET_AB="A/B Testing"
MOD_MOKOJOOMHERO_AB_ENABLED_LABEL="Enable A/B Testing"
MOD_MOKOJOOMHERO_AB_ENABLED_DESC="Randomly show different content variations to visitors."
MOD_MOKOJOOMHERO_AB_VARIATIONS_LABEL="Variations"
MOD_MOKOJOOMHERO_AB_VARIATIONS_DESC="Define content variations with relative weights."
MOD_MOKOJOOMHERO_AB_VAR_LABEL="Label"
MOD_MOKOJOOMHERO_AB_VAR_CONTENT="Content"
MOD_MOKOJOOMHERO_AB_VAR_WEIGHT="Weight"
; Scheduling
MOD_MOKOJOOMHERO_FIELDSET_SCHEDULING="Scheduling"
MOD_MOKOJOOMHERO_SCHEDULE_ENABLED_LABEL="Enable Scheduling"
MOD_MOKOJOOMHERO_SCHEDULE_ENABLED_DESC="Only display the hero during a specific date/time range."
MOD_MOKOJOOMHERO_SCHEDULE_START_LABEL="Start Date/Time"
MOD_MOKOJOOMHERO_SCHEDULE_START_DESC="The hero will not display before this date and time."
MOD_MOKOJOOMHERO_SCHEDULE_END_LABEL="End Date/Time"
MOD_MOKOJOOMHERO_SCHEDULE_END_DESC="The hero will not display after this date and time."
; Video poster
MOD_MOKOJOOMHERO_VIDEO_POSTER_LABEL="Video Poster Image"
MOD_MOKOJOOMHERO_VIDEO_POSTER_DESC="Fallback image displayed while the video loads."
@@ -107,6 +107,25 @@ MOD_MOKOJOOMHERO_PARALLAX_DESC="Background moves at a slower rate than page cont
MOD_MOKOJOOMHERO_PARALLAX_SPEED_LABEL="Parallax Speed"
MOD_MOKOJOOMHERO_PARALLAX_SPEED_DESC="How much the background moves relative to scroll (0.1 = subtle, 0.9 = dramatic)."
; A/B testing
MOD_MOKOJOOMHERO_FIELDSET_AB="A/B Testing"
MOD_MOKOJOOMHERO_AB_ENABLED_LABEL="Enable A/B Testing"
MOD_MOKOJOOMHERO_AB_ENABLED_DESC="Randomly show different content variations to visitors. Assignment is sticky per session."
MOD_MOKOJOOMHERO_AB_VARIATIONS_LABEL="Variations"
MOD_MOKOJOOMHERO_AB_VARIATIONS_DESC="Define content variations with relative weights. Higher weight = higher chance of being shown."
MOD_MOKOJOOMHERO_AB_VAR_LABEL="Label"
MOD_MOKOJOOMHERO_AB_VAR_CONTENT="Content"
MOD_MOKOJOOMHERO_AB_VAR_WEIGHT="Weight"
; Scheduling
MOD_MOKOJOOMHERO_FIELDSET_SCHEDULING="Scheduling"
MOD_MOKOJOOMHERO_SCHEDULE_ENABLED_LABEL="Enable Scheduling"
MOD_MOKOJOOMHERO_SCHEDULE_ENABLED_DESC="Only display the hero during a specific date/time range. Uses the site timezone."
MOD_MOKOJOOMHERO_SCHEDULE_START_LABEL="Start Date/Time"
MOD_MOKOJOOMHERO_SCHEDULE_START_DESC="The hero will not display before this date and time. Leave empty for no start restriction."
MOD_MOKOJOOMHERO_SCHEDULE_END_LABEL="End Date/Time"
MOD_MOKOJOOMHERO_SCHEDULE_END_DESC="The hero will not display after this date and time. Leave empty for no end restriction."
; Video poster
MOD_MOKOJOOMHERO_VIDEO_POSTER_LABEL="Video Poster Image"
MOD_MOKOJOOMHERO_VIDEO_POSTER_DESC="Fallback image displayed while the video loads. Prevents a blank hero on slow connections."
@@ -91,6 +91,25 @@ MOD_MOKOJOOMHERO_PARALLAX_DESC="Background moves at a slower rate than page cont
MOD_MOKOJOOMHERO_PARALLAX_SPEED_LABEL="Parallax Speed"
MOD_MOKOJOOMHERO_PARALLAX_SPEED_DESC="How much the background moves relative to scroll (0.1 = subtle, 0.9 = dramatic)."
; A/B testing
MOD_MOKOJOOMHERO_FIELDSET_AB="A/B Testing"
MOD_MOKOJOOMHERO_AB_ENABLED_LABEL="Enable A/B Testing"
MOD_MOKOJOOMHERO_AB_ENABLED_DESC="Randomly show different content variations to visitors."
MOD_MOKOJOOMHERO_AB_VARIATIONS_LABEL="Variations"
MOD_MOKOJOOMHERO_AB_VARIATIONS_DESC="Define content variations with relative weights."
MOD_MOKOJOOMHERO_AB_VAR_LABEL="Label"
MOD_MOKOJOOMHERO_AB_VAR_CONTENT="Content"
MOD_MOKOJOOMHERO_AB_VAR_WEIGHT="Weight"
; Scheduling
MOD_MOKOJOOMHERO_FIELDSET_SCHEDULING="Scheduling"
MOD_MOKOJOOMHERO_SCHEDULE_ENABLED_LABEL="Enable Scheduling"
MOD_MOKOJOOMHERO_SCHEDULE_ENABLED_DESC="Only display the hero during a specific date/time range."
MOD_MOKOJOOMHERO_SCHEDULE_START_LABEL="Start Date/Time"
MOD_MOKOJOOMHERO_SCHEDULE_START_DESC="The hero will not display before this date and time."
MOD_MOKOJOOMHERO_SCHEDULE_END_LABEL="End Date/Time"
MOD_MOKOJOOMHERO_SCHEDULE_END_DESC="The hero will not display after this date and time."
; Video poster
MOD_MOKOJOOMHERO_VIDEO_POSTER_LABEL="Video Poster Image"
MOD_MOKOJOOMHERO_VIDEO_POSTER_DESC="Fallback image displayed while the video loads."
@@ -23,6 +23,73 @@ $wa = $app->getDocument()->getWebAssetManager();
$wa->getRegistry()->addExtensionRegistryFile('mod_mokojoomhero');
$wa->usePreset('mod_mokojoomhero');
// Schedule check — skip rendering if outside the configured date range
$scheduleEnabled = (bool) $params->get('scheduleEnabled', 0);
if ($scheduleEnabled) {
$now = new \DateTime('now', new \DateTimeZone($app->get('offset', 'UTC')));
$scheduleStart = $params->get('scheduleStart', '');
$scheduleEnd = $params->get('scheduleEnd', '');
if ($scheduleStart) {
$start = new \DateTime($scheduleStart, new \DateTimeZone($app->get('offset', 'UTC')));
if ($now < $start) {
return;
}
}
if ($scheduleEnd) {
$end = new \DateTime($scheduleEnd, new \DateTimeZone($app->get('offset', 'UTC')));
if ($now > $end) {
return;
}
}
}
// A/B testing — weighted random variation, session-sticky per module instance
$abEnabled = (bool) $params->get('abEnabled', 0);
$abVariationContent = '';
if ($abEnabled) {
$abVariations = $params->get('abVariations', '');
$abData = is_string($abVariations) ? json_decode($abVariations, true) : (array) $abVariations;
if (is_array($abData) && count($abData) > 0) {
$session = \Joomla\CMS\Factory::getSession();
$sessionKey = 'mokojoomhero.ab.' . $module->id;
$picked = $session->get($sessionKey, null);
if ($picked === null || !isset($abData[$picked])) {
// Weighted random selection
$totalWeight = 0;
foreach ($abData as $v) {
$totalWeight += (int) (((array) $v)['weight'] ?? 50);
}
$rand = mt_rand(1, max($totalWeight, 1));
$cumulative = 0;
$picked = 0;
foreach ($abData as $i => $v) {
$cumulative += (int) (((array) $v)['weight'] ?? 50);
if ($rand <= $cumulative) {
$picked = $i;
break;
}
}
$session->set($sessionKey, $picked);
}
$variation = (array) ($abData[$picked] ?? []);
$abVariationContent = $variation['content'] ?? '';
}
}
// Module parameters
$heroMode = $params->get('heroMode', 'images');
$imageFolder = $params->get('imageFolder', 'images/heroes');
@@ -118,6 +185,11 @@ if (!in_array($contentAnimation, $allowedContentAnimations, true)) {
$parallaxSpeed = max(0.1, min(0.9, $parallaxSpeed));
$gradientAngle = max(0, min(360, $gradientAngle));
// Apply A/B variation content if active
if ($abEnabled && $abVariationContent) {
$heroContent = $abVariationContent;
}
// Collect hero images
$heroImages = [];
@@ -274,6 +274,89 @@
<option value="1">JYES</option>
</field>
</fieldset>
<fieldset name="abtesting"
label="MOD_MOKOJOOMHERO_FIELDSET_AB"
>
<field
name="abEnabled"
type="radio"
layout="joomla.form.field.radio.switcher"
label="MOD_MOKOJOOMHERO_AB_ENABLED_LABEL"
description="MOD_MOKOJOOMHERO_AB_ENABLED_DESC"
default="0"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
<field
name="abVariations"
type="subform"
label="MOD_MOKOJOOMHERO_AB_VARIATIONS_LABEL"
description="MOD_MOKOJOOMHERO_AB_VARIATIONS_DESC"
multiple="true"
layout="joomla.form.field.subform.repeatable-table"
showon="abEnabled:1"
max="4"
>
<form>
<field
name="label"
type="text"
label="MOD_MOKOJOOMHERO_AB_VAR_LABEL"
filter="string"
default="Variation"
/>
<field
name="content"
type="textarea"
label="MOD_MOKOJOOMHERO_AB_VAR_CONTENT"
filter="safehtml"
rows="3"
/>
<field
name="weight"
type="number"
label="MOD_MOKOJOOMHERO_AB_VAR_WEIGHT"
default="50"
min="1"
max="100"
/>
</form>
</field>
</fieldset>
<fieldset name="scheduling"
label="MOD_MOKOJOOMHERO_FIELDSET_SCHEDULING"
>
<field
name="scheduleEnabled"
type="radio"
layout="joomla.form.field.radio.switcher"
label="MOD_MOKOJOOMHERO_SCHEDULE_ENABLED_LABEL"
description="MOD_MOKOJOOMHERO_SCHEDULE_ENABLED_DESC"
default="0"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
<field
name="scheduleStart"
type="calendar"
label="MOD_MOKOJOOMHERO_SCHEDULE_START_LABEL"
description="MOD_MOKOJOOMHERO_SCHEDULE_START_DESC"
format="%Y-%m-%d %H:%M"
showtime="true"
showon="scheduleEnabled:1"
/>
<field
name="scheduleEnd"
type="calendar"
label="MOD_MOKOJOOMHERO_SCHEDULE_END_LABEL"
description="MOD_MOKOJOOMHERO_SCHEDULE_END_DESC"
format="%Y-%m-%d %H:%M"
showtime="true"
showon="scheduleEnabled:1"
/>
</fieldset>
<fieldset name="content"
label="MOD_MOKOJOOMHERO_FIELDSET_CONTENT"
>
+1 -1
View File
@@ -25,6 +25,6 @@
</files>
<updateservers>
<server type="extension" name="MokoJoomHero Updates">https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero/raw/branch/main/updates.xml</server>
<server type="extension" name="MokoJoomHero Updates">https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero/updates.xml</server>
</updateservers>
</extension>
+4 -4
View File
@@ -1,7 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
SPDX-License-Identifier: GPL-3.0-or-later
VERSION: 01.08.00
VERSION: 01.09.00
-->
<updates>
@@ -104,13 +104,13 @@
<element>pkg_mokojoomhero</element>
<type>package</type>
<client>site</client>
<version>01.08.00</version>
<version>01.09.00</version>
<creationDate>2026-06-04</creationDate>
<infourl title='Package - MokoJoomHero'>https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero/releases/tag/stable</infourl>
<downloads>
<downloadurl type='full' format='zip'>https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero/releases/download/stable/pkg_mokojoomhero-01.08.00.zip</downloadurl>
<downloadurl type='full' format='zip'>https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero/releases/download/stable/pkg_mokojoomhero-01.09.00.zip</downloadurl>
</downloads>
<sha256>111fef438f7c30a74019124134ed461e2b92f9db992c45eef7d5f63091a5e0d7</sha256>
<sha256>fd8235cee1a6f08a7821ccae95776f2f57e32ccaad5efe64ef8b377dd7ec47df</sha256>
<tags><tag>stable</tag></tags>
<changelogurl>https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero/raw/branch/main/CHANGELOG.md</changelogurl>
<maintainer>Moko Consulting</maintainer>