Compare commits

..

7 Commits

50 changed files with 404 additions and 665 deletions
+1 -1
View File
@@ -10,7 +10,7 @@
<display-name>Package - MokoJoomHero</display-name>
<org>MokoConsulting</org>
<description>A Joomla Module designed to provide a random image from a folder with content on top as a Hero.</description>
<version>01.14.00</version>
<version>01.08.00</version>
<license spdx="GPL-3.0-or-later">GNU General Public License v3</license>
</identity>
<governance>
+3 -5
View File
@@ -102,14 +102,13 @@ jobs:
run: |
php /tmp/moko-platform-api/cli/release_publish.php \
--path . --stability rc --bump minor --branch rc \
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
--skip-update-stream
--token "${{ secrets.MOKOGITEA_TOKEN }}"
- name: Summary
if: always()
run: |
echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY
echo "Branch renamed to rc, minor bump, RC release built (updates.xml managed by Gitea Pages)" >> $GITHUB_STEP_SUMMARY
echo "Branch renamed to rc, minor bump, RC + lesser stream releases built, updates.xml synced" >> $GITHUB_STEP_SUMMARY
# ── Merged PR → Build & Release (or promote RC to stable) ────────────────────
release:
@@ -168,8 +167,7 @@ jobs:
run: |
php /tmp/moko-platform-api/cli/release_publish.php \
--path . --stability stable --bump minor --branch main \
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
--skip-update-stream
--token "${{ secrets.MOKOGITEA_TOKEN }}"
# -- STEP 9: Mirror to GitHub (stable only) --------------------------------
- name: "Step 9: Mirror release to GitHub"
+1 -1
View File
@@ -5,7 +5,7 @@
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Automation
# VERSION: 01.14.00
# VERSION: 01.08.00
# BRIEF: Auto-create feature branch when an issue is opened
name: "Universal: Issue Branch"
-231
View File
@@ -147,98 +147,6 @@ jobs:
echo "PHP lint: ${ERRORS} error(s)"
[ "$ERRORS" -eq 0 ] || { echo "::error::PHP syntax errors found"; exit 1; }
- name: Joomla JEXEC guard check
if: steps.platform.outputs.platform == 'joomla'
run: |
ERRORS=0
while IFS= read -r -d '' file; do
# Skip vendor, node_modules, and index.html stub files
case "$file" in ./vendor/*|./node_modules/*) continue ;; esac
# Check first 10 lines for JEXEC or JPATH guard
if ! head -20 "$file" | grep -qE "defined\s*\(\s*['\"](_JEXEC|JPATH_BASE|\\\\JPATH_PLATFORM)['\"]"; then
echo "::error file=${file}::Missing JEXEC guard: ${file}"
ERRORS=$((ERRORS + 1))
fi
done < <(find . -name "*.php" -path "*/src/*" -not -path "./.git/*" -not -path "./vendor/*" -print0)
if [ "$ERRORS" -gt 0 ]; then
echo "::error::${ERRORS} PHP file(s) missing defined('_JEXEC') or die guard"
echo "## JEXEC Guard Check: Failed" >> $GITHUB_STEP_SUMMARY
echo "${ERRORS} file(s) in src/ are missing the Joomla execution guard." >> $GITHUB_STEP_SUMMARY
exit 1
fi
echo "JEXEC guard: OK"
- name: Joomla directory listing protection
if: steps.platform.outputs.platform == 'joomla'
run: |
MISSING=0
SOURCE_DIR="src"
[ ! -d "$SOURCE_DIR" ] && exit 0
while IFS= read -r dir; do
if [ ! -f "${dir}/index.html" ]; then
echo "::warning::Missing index.html in ${dir} (directory listing protection)"
MISSING=$((MISSING + 1))
fi
done < <(find "$SOURCE_DIR" -type d -not -path "./.git/*" -not -path "*/vendor/*" -not -path "*/node_modules/*")
if [ "$MISSING" -gt 0 ]; then
echo "## Directory Protection" >> $GITHUB_STEP_SUMMARY
echo "${MISSING} director(ies) missing index.html" >> $GITHUB_STEP_SUMMARY
fi
echo "Directory protection: ${MISSING} missing (advisory)"
- name: Joomla script file and asset checks
if: steps.platform.outputs.platform == 'joomla'
run: |
ERRORS=0
MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
[ -z "$MANIFEST" ] && exit 0
MANIFEST_DIR=$(dirname "$MANIFEST")
# Check scriptfile exists if declared
SCRIPTFILE=$(sed -n 's/.*<scriptfile>\([^<]*\)<\/scriptfile>.*/\1/p' "$MANIFEST" 2>/dev/null)
if [ -n "$SCRIPTFILE" ]; then
if [ ! -f "${MANIFEST_DIR}/${SCRIPTFILE}" ]; then
echo "::error::Manifest declares <scriptfile>${SCRIPTFILE}</scriptfile> but file not found at ${MANIFEST_DIR}/${SCRIPTFILE}"
ERRORS=$((ERRORS + 1))
else
echo "Script file: ${MANIFEST_DIR}/${SCRIPTFILE} (OK)"
fi
fi
# Require joomla.asset.json and validate it
ASSET_JSON=$(find "$MANIFEST_DIR" -name "joomla.asset.json" -not -path "./.git/*" 2>/dev/null | head -1)
if [ -z "$ASSET_JSON" ]; then
echo "::error::joomla.asset.json not found — Joomla asset system is required"
ERRORS=$((ERRORS + 1))
else
if command -v php &> /dev/null; then
php -r "json_decode(file_get_contents('$ASSET_JSON')); if(json_last_error()!==JSON_ERROR_NONE){echo json_last_error_msg();exit(1);}" 2>&1 || {
echo "::error::joomla.asset.json is not valid JSON"
ERRORS=$((ERRORS + 1))
}
fi
echo "joomla.asset.json: valid"
fi
# Validate all XML files in src/ are well-formed
XML_ERRORS=0
if command -v php &> /dev/null; then
while IFS= read -r -d '' xmlfile; do
if ! php -r "libxml_use_internal_errors(true); \$x = simplexml_load_file('$xmlfile'); if(!\$x){foreach(libxml_get_errors() as \$e) echo trim(\$e->message) . ' in $xmlfile'; exit(1);}" 2>&1; then
XML_ERRORS=$((XML_ERRORS + 1))
fi
done < <(find "$MANIFEST_DIR" -name "*.xml" -not -path "./.git/*" -print0)
fi
if [ "$XML_ERRORS" -gt 0 ]; then
echo "::error::${XML_ERRORS} XML file(s) are malformed"
ERRORS=$((ERRORS + 1))
else
echo "XML well-formedness: OK"
fi
[ "$ERRORS" -gt 0 ] && exit 1
echo "Joomla asset checks: OK"
- name: Validate platform manifest
run: |
PLATFORM="${{ steps.platform.outputs.platform }}"
@@ -256,13 +164,6 @@ jobs:
for ELEMENT in name version description; do
grep -q "<${ELEMENT}>" "$MANIFEST" || { echo "::error::Missing <${ELEMENT}> in manifest"; exit 1; }
done
# Block legacy raw/branch update server URLs on MokoGitea
RAW_URLS=$(grep -n 'raw/branch' "$MANIFEST" | grep -i 'mokoconsulting\|mokogitea\|git\.mokoconsulting\.tech' || true)
if [ -n "$RAW_URLS" ]; then
echo "::error::Manifest contains legacy raw/branch update server URL on MokoGitea. Use the Gitea Pages URL instead (e.g. /{REPO}/updates.xml not /{REPO}/raw/branch/main/updates.xml)"
echo "$RAW_URLS"
exit 1
fi
echo "Joomla manifest valid"
;;
dolibarr)
@@ -295,138 +196,6 @@ jobs:
;;
esac
- name: Validate Joomla language files
if: steps.platform.outputs.platform == 'joomla'
run: |
ERRORS=0
WARNINGS=0
# Require both en-GB and en-US language directories
LANG_ROOT=$(find . -path "*/language" -type d -not -path "./.git/*" 2>/dev/null | head -1)
if [ -z "$LANG_ROOT" ]; then
echo "No language/ directory found — skipping"
exit 0
fi
if [ ! -d "$LANG_ROOT/en-GB" ]; then
echo "::error::Missing en-GB language directory (${LANG_ROOT}/en-GB)"
ERRORS=$((ERRORS + 1))
fi
if [ ! -d "$LANG_ROOT/en-US" ]; then
echo "::error::Missing en-US language directory (${LANG_ROOT}/en-US)"
ERRORS=$((ERRORS + 1))
fi
# Check that en-GB and en-US have matching .ini files
if [ -d "$LANG_ROOT/en-GB" ] && [ -d "$LANG_ROOT/en-US" ]; then
for GB_INI in "$LANG_ROOT/en-GB"/*.ini; do
[ ! -f "$GB_INI" ] && continue
US_INI="$LANG_ROOT/en-US/$(basename "$GB_INI")"
if [ ! -f "$US_INI" ]; then
echo "::error::$(basename "$GB_INI") exists in en-GB but missing from en-US"
ERRORS=$((ERRORS + 1))
fi
done
for US_INI in "$LANG_ROOT/en-US"/*.ini; do
[ ! -f "$US_INI" ] && continue
GB_INI="$LANG_ROOT/en-GB/$(basename "$US_INI")"
if [ ! -f "$GB_INI" ]; then
echo "::error::$(basename "$US_INI") exists in en-US but missing from en-GB"
ERRORS=$((ERRORS + 1))
fi
done
fi
# Find all .ini language files
INI_FILES=$(find . -path "*/language/*/*.ini" -not -path "./.git/*" 2>/dev/null)
if [ -z "$INI_FILES" ]; then
echo "No .ini language files found"
[ "$ERRORS" -gt 0 ] && exit 1
exit 0
fi
echo "Found $(echo "$INI_FILES" | wc -l) language file(s)"
for FILE in $INI_FILES; do
FNAME=$(basename "$FILE")
LINENUM=0
SEEN_KEYS=""
while IFS= read -r line || [ -n "$line" ]; do
LINENUM=$((LINENUM + 1))
# Skip empty lines and comments
[ -z "$line" ] && continue
echo "$line" | grep -qE '^\s*;' && continue
echo "$line" | grep -qE '^\s*$' && continue
# Must match KEY="VALUE" format
if ! echo "$line" | grep -qE '^[A-Z_][A-Z0-9_]*=".*"$'; then
echo "::error file=${FILE},line=${LINENUM}::Malformed line: ${line}"
ERRORS=$((ERRORS + 1))
continue
fi
# Extract key and check for duplicates
KEY=$(echo "$line" | sed 's/=.*//')
if echo "$SEEN_KEYS" | grep -qx "$KEY"; then
echo "::error file=${FILE},line=${LINENUM}::Duplicate key: ${KEY}"
ERRORS=$((ERRORS + 1))
fi
SEEN_KEYS="${SEEN_KEYS}
${KEY}"
done < "$FILE"
echo " ${FILE}: checked ${LINENUM} lines"
done
# Cross-check en-GB vs en-US key consistency
GB_DIR=$(find . -path "*/language/en-GB" -type d -not -path "./.git/*" 2>/dev/null | head -1)
US_DIR=$(find . -path "*/language/en-US" -type d -not -path "./.git/*" 2>/dev/null | head -1)
if [ -n "$GB_DIR" ] && [ -n "$US_DIR" ]; then
for GB_FILE in "$GB_DIR"/*.ini; do
[ ! -f "$GB_FILE" ] && continue
FNAME=$(basename "$GB_FILE")
US_FILE="$US_DIR/$FNAME"
[ ! -f "$US_FILE" ] && continue
GB_KEYS=$(grep -oP '^[A-Z_][A-Z0-9_]*(?==)' "$GB_FILE" 2>/dev/null | sort)
US_KEYS=$(grep -oP '^[A-Z_][A-Z0-9_]*(?==)' "$US_FILE" 2>/dev/null | sort)
# Keys in en-GB but not en-US
MISSING_US=$(comm -23 <(echo "$GB_KEYS") <(echo "$US_KEYS"))
if [ -n "$MISSING_US" ]; then
echo "::warning::Keys in en-GB/$FNAME but missing from en-US/$FNAME:"
echo "$MISSING_US" | while read -r k; do echo " - $k"; done
WARNINGS=$((WARNINGS + 1))
fi
# Keys in en-US but not en-GB
MISSING_GB=$(comm -13 <(echo "$GB_KEYS") <(echo "$US_KEYS"))
if [ -n "$MISSING_GB" ]; then
echo "::warning::Keys in en-US/$FNAME but missing from en-GB/$FNAME:"
echo "$MISSING_GB" | while read -r k; do echo " - $k"; done
WARNINGS=$((WARNINGS + 1))
fi
done
fi
{
echo "### Language File Validation"
echo "| Metric | Count |"
echo "|---|---|"
echo "| Files checked | $(echo "$INI_FILES" | wc -l) |"
echo "| Errors | ${ERRORS} |"
echo "| Warnings | ${WARNINGS} |"
} >> $GITHUB_STEP_SUMMARY
if [ "$ERRORS" -gt 0 ]; then
echo "::error::Language validation failed with ${ERRORS} error(s)"
exit 1
fi
echo "Language files: OK (${WARNINGS} warning(s))"
- name: Check changelog has unreleased entry
run: |
if [ ! -f "CHANGELOG.md" ]; then
+34 -105
View File
@@ -2,116 +2,45 @@
## [Unreleased]
## [01.14] - 2026-06-04
### Changed
- Update server URL to new Gitea release system
- Use pretty name format for update server entry
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
Version format: `XX.YY.ZZ` (zero-padded semver).
## [01.08.00] --- 2026-06-04
## [01.04.00] - 2026-05-30
### Added
- Local Video hero mode with Joomla Media Manager file picker (`localVideoFile` param)
- Install script (`script.php`) creates `images/heroes/` folder on install/update
## [01.03.00] - 2026-05-30
### Added
- Configurable card fade-in delay with slide-up animation (`cardDelay` param, 0-5000ms) (#39)
- Video mute/unmute toggle button (`showMuteToggle` param) -- supports YouTube, Vimeo, and native video (#40)
## [01.02.00] - 2026-05-30
### Fixed
- Add missing `<updateservers>` section to module manifest
- WebAsset registration: added `#style`/`#script` suffixes to preset dependencies (was causing `UnsatisfiedDependencyException`)
- Asset URI resolution: use `extension/filename` pattern instead of `extension/folder/filename` (Joomla auto-inserts `css/`/`js/` folders)
- iframe video cover: split CSS into `<video>` (`object-fit: cover`) and `<iframe>` (oversize + centre-crop) rules since `object-fit` doesn't apply to iframes
- Card link styling: exclude `.btn` elements from `color: inherit` so buttons retain their own styles
## [01.13] - 2026-06-04
### Added
- Module title renders inside the hero card as `<h2>`, respecting Joomla's Show Title toggle
- IntersectionObserver pauses/resumes videos when the hero scrolls out of/into the viewport (YouTube, Vimeo, and native `<video>`)
- YouTube embeds include `enablejsapi=1` for postMessage playback control
## [01.01.00] - 2026-05-30
### Changed
- Restructure from package extension back to standalone site module
- Remove system plugin (`plg_system_mokojoomhero`) and package wrapper
- Simplify build target for module ZIP
## [01.12] - 2026-06-04
### Changed
- Remove static `updates.xml` and `update.xml` from repository
- Update server now managed by Gitea release system
## [01.11] - 2026-06-04
### Changed
- Clean up `updates.xml` — remove stale dev/alpha/beta/rc entries
## [01.10] - 2026-06-04
- Migrated all workflow and template paths from `.github/` to `.mokogitea/`
- Template source paths updated
- HCL definition files removed -- Template repos are now the canonical source
### Added
- A/B testing with weighted random variation selection and session-sticky assignment (#21)
- Hero scheduling with start/end datetime fields and site timezone support (#22)
## [01.09] - 2026-06-04
### Added
- Licensing system with free/pro tier constant (free tier — no key required)
## [01.08] - 2026-06-04
### Added
- Solid colour and gradient hero background modes
- Configurable slide transitions: crossfade, slide, fade-to-black, zoom (Ken Burns)
- Vertical text alignment (top/center/bottom) (#53)
- Mobile-specific hero height via CSS custom property (#55)
- Gradient overlay direction (dark at bottom/top/left/right) (#54)
- Prefers-reduced-motion accessibility support (WCAG 2.1 AA) (#49)
- Scroll-down indicator with bounce animation (#50)
- Video poster image fallback (#51)
- Content entrance animations: fade-in, slide-up, slide-left, slide-right (#52)
- Parallax scroll effect with configurable speed (#48)
- Article content source — pull hero content from a Joomla article (#56)
- Per-slide unique content via subform repeatable field (#57)
### Changed
- Restructure from standalone module to package extension with system plugin
- Hardcode package description in manifest
### Fixed
- CSS injection prevention on `heroHeight` with regex validation
- Hex colour validation for all colour parameters
- Allowlist validation for textAlign, fadeType, overlayType
- `video.play()` promise rejection handling
- `iframe.contentWindow` null guards on postMessage calls
- `DirectoryIterator` try-catch for filesystem errors
- Article content filtered through `HTMLHelper::content.prepare` for XSS prevention
- Null guards on scroll indicator and mute toggle DOM elements
- Logging for article query, image folder, and JSON decode failures
- Plugin manifest `<languages>` folder attribute and display name
- Missing language keys in `.sys.ini` files
- SPDX license headers on all PHP files
- Consistent tab indentation throughout
## [01.07] - 2026-06-02
### Added
- Package extension structure (`pkg_mokojoomhero`)
- System plugin for license key validation
## [01.06] - 2026-05-30
### Added
- Deploy target in Makefile with language file sync
## [01.05] - 2026-05-30
### Added
- Card fade-in delay with configurable timing
- Video mute/unmute toggle button
## [01.04] - 2026-05-30
### Added
- Local video mode with Joomla Media Manager picker
## [01.03] - 2026-05-30
### Added
- Video pause/resume on viewport exit via IntersectionObserver
- Module title rendered inside hero card
- WebAsset registration fix and iframe video cover
## [01.02] - 2026-05-30
### Added
- Card fade-in delay and video mute toggle (#39, #40)
## [01.01] - 2026-05-30
### Fixed
- WebAsset registration and iframe video cover
- Hero card title rendering
- Video viewport pause/resume
- `branch-cleanup.yml`: auto-delete merged feature branches after PR merge
+16 -9
View File
@@ -32,21 +32,25 @@ composer install # Install PHP dependencies
## Architecture
This is a Joomla **site module** (`mod_mokojoomhero`).
This is a Joomla **package** extension (`pkg_mokojoomhero`) containing two sub-extensions:
### 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
- A/B testing with weighted variations, scheduling with start/end datetime
- Article content source, per-slide unique content via subform
- Works independently — no plugin dependency required
### plg_system_mokojoomhero (System Plugin)
- Placeholder for future system-level features
- Auto-enabled on package install via `pkg_script.php`
- Namespace: `Joomla\Plugin\System\MokoJoomHero`
### Key files
- `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)
- `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)
## Rules
@@ -61,4 +65,7 @@ This is a Joomla **site module** (`mod_mokojoomhero`).
## 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
+1 -1
View File
@@ -14,7 +14,7 @@
DEFGROUP:
INGROUP: Project.Documentation
REPO:
VERSION: 01.14.00
VERSION: 01.08.00
PATH: ./CODE_OF_CONDUCT.md
BRIEF: Reference + packaging repo for Moko Consulting Developer GPT Other Default
-->
+24 -6
View File
@@ -13,7 +13,7 @@
# Extension Configuration
EXTENSION_NAME := mokojoomhero
EXTENSION_TYPE := module
EXTENSION_TYPE := package
# Options: module, plugin, component, package, template
EXTENSION_VERSION := 1.0.0
@@ -156,11 +156,29 @@ clean: ## Clean build artifacts
.PHONY: build
build: clean ## Build extension package
@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)"
@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
.PHONY: package
package: build ## Alias for build
+1 -1
View File
@@ -7,7 +7,7 @@
# FILE INFORMATION
REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero
FILE: ./README.md
VERSION: 01.14.00
VERSION: 01.08.00
BRIEF: MokoJoomHero - Joomla Module
-->
+1 -1
View File
@@ -23,7 +23,7 @@ DEFGROUP: [PROJECT_NAME]
INGROUP: [PROJECT_NAME].Documentation
REPO: [REPOSITORY_URL]
PATH: /SECURITY.md
VERSION: 01.14.00
VERSION: 01.08.00
BRIEF: Security vulnerability reporting and handling policy
-->
@@ -6,7 +6,7 @@
; INGROUP: MokoJoomHero.Module
; REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero
; PATH: /src/language/en-GB/mod_mokojoomhero.ini
; VERSION: 01.14.00
; VERSION: 01.08.00
; BRIEF: Language strings for MokoJoomHero module (frontend + admin form fields)
MOD_MOKOJOOMHERO_NO_CONTENT="Add content to this module to display it over the hero image."
@@ -90,25 +90,6 @@ 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."
@@ -6,7 +6,7 @@
; INGROUP: MokoJoomHero.Module
; REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero
; PATH: /src/language/en-GB/mod_mokojoomhero.sys.ini
; VERSION: 01.14.00
; VERSION: 01.08.00
; BRIEF: System language strings — used in admin Extension Manager and Module Manager
MOD_MOKOJOOMHERO="Module - MokoJoomHero"
@@ -91,25 +91,6 @@ 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."
@@ -6,7 +6,7 @@
; INGROUP: MokoJoomHero.Module
; REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero
; PATH: /src/language/en-US/mod_mokojoomhero.ini
; VERSION: 01.14.00
; VERSION: 01.08.00
; BRIEF: Language strings for MokoJoomHero module (en-US, frontend + admin form fields)
MOD_MOKOJOOMHERO_NO_CONTENT="Add content to this module to display it over the hero image."
@@ -107,25 +107,6 @@ 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."
@@ -6,7 +6,7 @@
; INGROUP: MokoJoomHero.Module
; REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero
; PATH: /src/language/en-US/mod_mokojoomhero.sys.ini
; VERSION: 01.14.00
; VERSION: 01.08.00
; BRIEF: System language strings — used in admin Extension Manager and Module Manager (en-US)
MOD_MOKOJOOMHERO="Module - MokoJoomHero"
@@ -91,25 +91,6 @@ 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."
@@ -6,8 +6,8 @@
* DEFGROUP: MokoJoomHero.Module.Assets
* INGROUP: MokoJoomHero.Module
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero
* PATH: /src/media/css/mod_mokojoomhero.css
* VERSION: 01.14.00
* PATH: /src/packages/mod_mokojoomhero/media/css/mod_mokojoomhero.css
* VERSION: 01.08.00
* BRIEF: Hero module stylesheet slideshow, video, colour/gradient, overlay, card, mute toggle, responsive
*/
@@ -0,0 +1 @@
<html><body bgcolor="#FFFFFF"></body></html>
@@ -0,0 +1 @@
<html><body bgcolor="#FFFFFF"></body></html>
@@ -7,8 +7,8 @@
* DEFGROUP: MokoJoomHero.Module.Assets
* INGROUP: MokoJoomHero.Module
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero
* PATH: /src/media/js/mod_mokojoomhero.js
* VERSION: 01.14.00
* PATH: /src/packages/mod_mokojoomhero/media/js/mod_mokojoomhero.js
* VERSION: 01.08.00
* BRIEF: Hero module JavaScript slideshow crossfade, video viewport control, mute toggle
*/
@@ -23,73 +23,6 @@ $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');
@@ -185,11 +118,6 @@ 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 = [];
@@ -10,19 +10,19 @@
DEFGROUP: MokoJoomHero.Module
INGROUP: MokoJoomHero
REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero
PATH: /src/mod_mokojoomhero.xml
PATH: /src/packages/mod_mokojoomhero/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-06-04</creationDate>
<creationDate>2026-05</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>
<version>01.14.00</version>
<version>01.08.00-rc</version>
<description>Displays a random hero image slideshow or background video with content overlaid. Designed for MokoOnyx template. By Moko Consulting.</description>
<scriptfile>script.php</scriptfile>
@@ -48,10 +48,6 @@
<language tag="en-US">en-US/mod_mokojoomhero.sys.ini</language>
</languages>
<updateservers>
<server type="extension" name="Module - MokoJoomHero">https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero/updates.xml</server>
</updateservers>
<config>
<fields name="params">
<fieldset name="basic">
@@ -278,89 +274,6 @@
<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"
>
+27
View File
@@ -0,0 +1,27 @@
<?php
/**
* @package Joomla.Site
* @subpackage mod_mokojoomhero
*
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
* @license GPL-3.0-or-later
* SPDX-License-Identifier: GPL-3.0-or-later
*/
defined('_JEXEC') or die;
use Joomla\CMS\Filesystem\Folder;
use Joomla\CMS\Installer\InstallerAdapter;
class Mod_MokojoomheroInstallerScript
{
public function postflight(string $type, InstallerAdapter $adapter): void
{
$heroesPath = JPATH_ROOT . '/images/heroes';
if (!is_dir($heroesPath)) {
Folder::create($heroesPath);
}
}
}
@@ -0,0 +1 @@
<html><body bgcolor="#FFFFFF"></body></html>
@@ -0,0 +1 @@
<html><body bgcolor="#FFFFFF"></body></html>
@@ -0,0 +1,2 @@
; Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
; SPDX-License-Identifier: GPL-3.0-or-later
@@ -0,0 +1,5 @@
; 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"
@@ -0,0 +1 @@
<html><body bgcolor="#FFFFFF"></body></html>
@@ -0,0 +1,2 @@
; Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
; SPDX-License-Identifier: GPL-3.0-or-later
@@ -0,0 +1,5 @@
; 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"
@@ -0,0 +1 @@
<html><body bgcolor="#FFFFFF"></body></html>
@@ -0,0 +1,12 @@
<?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;
@@ -0,0 +1,34 @@
<?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.08.00-rc</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>
@@ -0,0 +1 @@
<html><body bgcolor="#FFFFFF"></body></html>
@@ -0,0 +1,39 @@
<?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;
}
);
}
};
@@ -0,0 +1,25 @@
<?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\Plugin\CMSPlugin;
use Joomla\Event\SubscriberInterface;
class MokoJoomHero extends CMSPlugin implements SubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [];
}
}
@@ -0,0 +1 @@
<html><body bgcolor="#FFFFFF"></body></html>
@@ -0,0 +1 @@
<html><body bgcolor="#FFFFFF"></body></html>
+30
View File
@@ -0,0 +1,30 @@
<?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.08.00-rc</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/raw/branch/main/updates.xml</server>
</updateservers>
</extension>
+59
View File
@@ -0,0 +1,59 @@
<?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'
);
}
}
}
}
-58
View File
@@ -1,58 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage mod_mokojoomhero
*
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
* @license GPL-3.0-or-later
* SPDX-License-Identifier: GPL-3.0-or-later
*/
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Filesystem\Folder;
use Joomla\CMS\Installer\InstallerAdapter;
class Mod_MokojoomheroInstallerScript
{
public function postflight(string $type, InstallerAdapter $adapter): void
{
// Create default image folder on fresh install
$heroesPath = JPATH_ROOT . '/images/heroes';
if (!is_dir($heroesPath)) {
Folder::create($heroesPath);
}
// Remove deprecated system plugin (was part of the old package extension)
$this->removeDeprecatedPlugin();
}
/**
* Uninstall plg_system_mokojoomhero if it exists — no longer needed.
*/
private function removeDeprecatedPlugin(): void
{
try {
$db = Factory::getDbo();
$query = $db->getQuery(true)
->select($db->quoteName('extension_id'))
->from($db->quoteName('#__extensions'))
->where($db->quoteName('type') . ' = ' . $db->quote('plugin'))
->where($db->quoteName('folder') . ' = ' . $db->quote('system'))
->where($db->quoteName('element') . ' = ' . $db->quote('mokojoomhero'));
$db->setQuery($query);
$extensionId = (int) $db->loadResult();
if ($extensionId) {
$installer = new \Joomla\CMS\Installer\Installer();
$installer->uninstall('plugin', $extensionId);
}
} catch (\Exception $e) {
// Non-critical — plugin may already be gone
}
}
}
+35
View File
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Joomla Extension Update Server XML
See: https://docs.joomla.org/Deploying_an_Update_Server
This file is the update server manifest for mod_mokojoomhero.
The Joomla installer polls this URL to check for new versions.
The manifest in this repository must reference this file:
<updateservers>
<server type="extension" priority="1" name="MokoJoomHero Updates">
https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero/raw/branch/main/updates.xml
</server>
</updateservers>
When a new release is made, run `make release` or the release workflow to
prepend a new <update> entry to this file automatically.
-->
<updates>
<update>
<name>Module - MokoJoomHero</name>
<description>MokoJoomHero — A Joomla hero image module by Moko Consulting</description>
<element>mod_mokojoomhero</element>
<type>module</type>
<client>site</client>
<version>{{VERSION}}</version>
<downloads>
<downloadurl type="full" format="zip">
https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero/releases/download/v{{VERSION}}/mod_mokojoomhero.zip
</downloadurl>
</downloads>
<targetplatform name="joomla" version="(5|6).*" />
<php_minimum>8.1</php_minimum>
</update>
</updates>
+27
View File
@@ -0,0 +1,27 @@
<?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.12.00
-->
<updates>
<update>
<name>Package - MokoJoomHero</name>
<description>Package - MokoJoomHero stable build.</description>
<element>pkg_mokojoomhero</element>
<type>package</type>
<client>site</client>
<version>01.12.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.12.00.zip</downloadurl>
</downloads>
<sha256>675e97f0f66e206b09a256152efdccd8afca979d7d68321a9931eb3d65fb3e0d</sha256>
<tags><tag>stable</tag></tags>
<changelogurl>https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero/raw/branch/main/CHANGELOG.md</changelogurl>
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
<targetplatform name="joomla" version="(5|6)\..*" />
</update>
</updates>