diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml
index 6305d8e..016f7ff 100644
--- a/.mokogitea/manifest.xml
+++ b/.mokogitea/manifest.xml
@@ -10,7 +10,7 @@
Package - MokoJoomHero
MokoConsulting
A Joomla Module designed to provide a random image from a folder with content on top as a Hero.
- 01.19.00
+ 01.20.00
GNU General Public License v3
diff --git a/.mokogitea/update-server.yml b/.mokogitea/update-server.yml
deleted file mode 100644
index e6a1924..0000000
--- a/.mokogitea/update-server.yml
+++ /dev/null
@@ -1,464 +0,0 @@
-# Copyright (C) 2026 Moko Consulting
-#
-# SPDX-License-Identifier: GPL-3.0-or-later
-#
-# FILE INFORMATION
-# DEFGROUP: Gitea.Workflow
-# INGROUP: MokoStandards.Joomla
-# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/MokoStandards-API
-# PATH: /templates/workflows/joomla/update-server.yml.template
-# VERSION: 04.06.00
-# BRIEF: Update Joomla update server XML feed with stable/rc/dev entries
-#
-# Writes updates.xml with multiple entries:
-# - stable on push to main (from auto-release)
-# - rc on push to rc/**
-# - development on push to dev or dev/**
-#
-# Joomla filters by user's "Minimum Stability" setting.
-
-name: Update Joomla Update Server XML Feed
-
-on:
- push:
- branches:
- - 'dev'
- - 'dev/**'
- - 'alpha/**'
- - 'beta/**'
- - 'rc/**'
- paths:
- - 'src/**'
- - 'htdocs/**'
- pull_request:
- types: [closed]
- branches:
- - 'dev'
- - 'dev/**'
- - 'alpha/**'
- - 'beta/**'
- - 'rc/**'
- paths:
- - 'src/**'
- - 'htdocs/**'
- workflow_dispatch:
- inputs:
- stability:
- description: 'Stability tag'
- required: true
- default: 'development'
- type: choice
- options:
- - development
- - alpha
- - beta
- - rc
- - stable
-
-env:
- FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
- GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
- GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
- GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
-
-permissions:
- contents: write
-
-jobs:
- update-xml:
- name: Update updates.xml
- runs-on: release
- if: >-
- github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' || github.event_name == 'push'
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- with:
- token: ${{ secrets.GA_TOKEN }}
- fetch-depth: 0
-
- - name: Setup MokoStandards tools
- env:
- MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN }}
- MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
- COMPOSER_AUTH: '{"http-basic":{"git.mokoconsulting.tech":{"username":"token","password":"${{ secrets.GA_TOKEN }}"}}}'
- run: |
- if ! command -v composer &> /dev/null; then
- sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1
- fi
- git clone --depth 1 --branch main --quiet \
- "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/MokoStandards-API.git" \
- /tmp/mokostandards-api 2>/dev/null || true
- if [ -d "/tmp/mokostandards-api" ] && [ -f "/tmp/mokostandards-api/composer.json" ]; then
- cd /tmp/mokostandards-api && composer install --no-dev --no-interaction --quiet 2>/dev/null || true
- fi
-
- - name: Generate updates.xml entry
- id: update
- run: |
- BRANCH="${{ github.ref_name }}"
- REPO="${{ github.repository }}"
- API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
- VERSION=$(php /tmp/mokostandards-api/cli/version_read.php --path . 2>/dev/null || echo "0.0.0")
-
- # Auto-bump patch on all branches (dev, alpha, beta, rc)
- git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
- git config --local user.name "gitea-actions[bot]"
- BUMPED=$(php /tmp/mokostandards-api/cli/version_bump.php --path . 2>/dev/null || true)
- if [ -n "$BUMPED" ]; then
- VERSION=$(php /tmp/mokostandards-api/cli/version_read.php --path . 2>/dev/null || echo "$VERSION")
- git add -A
- git commit -m "chore(version): auto-bump patch ${VERSION} [skip ci]" \
- --author="gitea-actions[bot] " 2>/dev/null || true
- git push 2>/dev/null || true
- fi
-
- # Determine stability from branch or input
- if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
- STABILITY="${{ inputs.stability }}"
- elif [[ "$BRANCH" == rc/* ]]; then
- STABILITY="rc"
- elif [[ "$BRANCH" == beta/* ]]; then
- STABILITY="beta"
- elif [[ "$BRANCH" == alpha/* ]]; then
- STABILITY="alpha"
- elif [[ "$BRANCH" == dev/* ]] || [[ "$BRANCH" == "dev" ]]; then
- STABILITY="development"
- else
- STABILITY="stable"
- fi
-
- echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT"
-
- # Parse manifest (portable — no grep -P)
- MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" ! -path "./build/*" -exec grep -l '/dev/null | head -1)
- if [ -z "$MANIFEST" ]; then
- echo "No Joomla manifest found — skipping"
- exit 0
- fi
-
- # Extract fields using sed (works on all runners)
- EXT_NAME=$(sed -n 's/.*\([^<]*\)<\/name>.*/\1/p' "$MANIFEST" | head -1)
- EXT_TYPE=$(sed -n 's/.*]*type="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
- EXT_ELEMENT=$(sed -n 's/.*\([^<]*\)<\/element>.*/\1/p' "$MANIFEST" | head -1)
- EXT_CLIENT=$(sed -n 's/.*]*client="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
- EXT_FOLDER=$(sed -n 's/.*]*group="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
- EXT_VERSION=$(sed -n 's/.*\([^<]*\)<\/version>.*/\1/p' "$MANIFEST" | head -1)
- TARGET_PLATFORM=$(sed -n 's/.*\(\).*/\1/p' "$MANIFEST" | head -1)
- PHP_MINIMUM=$(sed -n 's/.*\([^<]*\)<\/php_minimum>.*/\1/p' "$MANIFEST" | head -1)
-
- # Fallbacks
- [ -z "$EXT_NAME" ] && EXT_NAME="${{ github.event.repository.name }}"
- [ -z "$EXT_TYPE" ] && EXT_TYPE="component"
-
- # Derive element if not in manifest: try XML filename, then repo name
- if [ -z "$EXT_ELEMENT" ]; then
- EXT_ELEMENT=$(basename "$MANIFEST" .xml | tr '[:upper:]' '[:lower:]')
- case "$EXT_ELEMENT" in
- templatedetails|manifest|*.xml) EXT_ELEMENT=$(echo "${{ github.event.repository.name }}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') ;;
- esac
- fi
-
- # Use manifest version if README version is empty
- [ "$VERSION" = "0.0.0" ] && [ -n "$EXT_VERSION" ] && VERSION="$EXT_VERSION"
-
- [ -z "$TARGET_PLATFORM" ] && TARGET_PLATFORM=$(printf '' "/")
-
- CLIENT_TAG=""
- [ -n "$EXT_CLIENT" ] && CLIENT_TAG="${EXT_CLIENT}"
- [ -z "$CLIENT_TAG" ] && ([ "$EXT_TYPE" = "module" ] || [ "$EXT_TYPE" = "plugin" ]) && CLIENT_TAG="site"
-
- FOLDER_TAG=""
- [ -n "$EXT_FOLDER" ] && [ "$EXT_TYPE" = "plugin" ] && FOLDER_TAG="${EXT_FOLDER}"
-
- PHP_TAG=""
- [ -n "$PHP_MINIMUM" ] && PHP_TAG="${PHP_MINIMUM}"
-
- # Version suffix for non-stable
- DISPLAY_VERSION="$VERSION"
- case "$STABILITY" in
- development) DISPLAY_VERSION="${VERSION}-dev" ;;
- alpha) DISPLAY_VERSION="${VERSION}-alpha" ;;
- beta) DISPLAY_VERSION="${VERSION}-beta" ;;
- rc) DISPLAY_VERSION="${VERSION}-rc" ;;
- esac
-
- MAJOR=$(echo "$VERSION" | awk -F. '{print $1}')
-
- # Each stability level has its own release tag
- case "$STABILITY" in
- development) RELEASE_TAG="development" ;;
- alpha) RELEASE_TAG="alpha" ;;
- beta) RELEASE_TAG="beta" ;;
- rc) RELEASE_TAG="release-candidate" ;;
- *) RELEASE_TAG="v${MAJOR}" ;;
- esac
-
- PACKAGE_NAME="${EXT_ELEMENT}-${DISPLAY_VERSION}.zip"
- DOWNLOAD_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/download/${RELEASE_TAG}/${PACKAGE_NAME}"
- INFO_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}"
-
- # -- Build install packages (ZIP + tar.gz) --------------------
- SOURCE_DIR="src"
- [ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
- if [ -d "$SOURCE_DIR" ]; then
- EXCLUDES=".ftpignore sftp-config* *.ppk *.pem *.key .env*"
- TAR_NAME="${EXT_ELEMENT}-${DISPLAY_VERSION}.tar.gz"
-
- cd "$SOURCE_DIR"
- zip -r "/tmp/${PACKAGE_NAME}" . -x $EXCLUDES
- cd ..
- tar -czf "/tmp/${TAR_NAME}" -C "$SOURCE_DIR" \
- --exclude='.ftpignore' --exclude='sftp-config*' \
- --exclude='*.ppk' --exclude='*.pem' --exclude='*.key' --exclude='.env*' .
-
- SHA256=$(sha256sum "/tmp/${PACKAGE_NAME}" | cut -d' ' -f1)
-
- # Ensure release exists on Gitea
- RELEASE_JSON=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
- "${API_BASE}/releases/tags/${RELEASE_TAG}" 2>/dev/null || true)
- RELEASE_ID=$(echo "$RELEASE_JSON" | python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
-
- if [ -z "$RELEASE_ID" ]; then
- # Create release
- RELEASE_JSON=$(curl -sf -X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
- -H "Content-Type: application/json" \
- "${API_BASE}/releases" \
- -d "$(python3 -c "import json; print(json.dumps({
- 'tag_name': '${RELEASE_TAG}',
- 'name': '${RELEASE_TAG} (${DISPLAY_VERSION})',
- 'body': '${STABILITY} release',
- 'prerelease': True,
- 'target_commitish': 'main'
- }))")" 2>/dev/null || true)
- RELEASE_ID=$(echo "$RELEASE_JSON" | python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
- fi
-
- if [ -n "$RELEASE_ID" ]; then
- # Delete existing assets with same name before uploading
- ASSETS=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
- "${API_BASE}/releases/${RELEASE_ID}/assets" 2>/dev/null || echo "[]")
- for ASSET_FILE in "$PACKAGE_NAME" "$TAR_NAME"; do
- ASSET_ID=$(echo "$ASSETS" | python3 -c "
- import sys,json
- assets = json.load(sys.stdin)
- for a in assets:
- if a['name'] == '${ASSET_FILE}':
- print(a['id']); break
- " 2>/dev/null || true)
- if [ -n "$ASSET_ID" ]; then
- curl -sf -X DELETE -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
- "${API_BASE}/releases/${RELEASE_ID}/assets/${ASSET_ID}" 2>/dev/null || true
- fi
- done
-
- # Upload both formats
- curl -sf -X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
- -H "Content-Type: application/octet-stream" \
- --data-binary @"/tmp/${PACKAGE_NAME}" \
- "${API_BASE}/releases/${RELEASE_ID}/assets?name=${PACKAGE_NAME}" > /dev/null 2>&1 || true
-
- curl -sf -X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
- -H "Content-Type: application/octet-stream" \
- --data-binary @"/tmp/${TAR_NAME}" \
- "${API_BASE}/releases/${RELEASE_ID}/assets?name=${TAR_NAME}" > /dev/null 2>&1 || true
- fi
-
- echo "Packages: ${PACKAGE_NAME} + ${TAR_NAME} (SHA: ${SHA256})" >> $GITHUB_STEP_SUMMARY
- else
- SHA256=""
- fi
-
- # -- Build the new entry (canonical format matching release.yml) --
- NEW_ENTRY=""
- NEW_ENTRY="${NEW_ENTRY} \n"
- NEW_ENTRY="${NEW_ENTRY} ${EXT_NAME}\n"
- NEW_ENTRY="${NEW_ENTRY} ${EXT_NAME} ${STABILITY} build.\n"
- NEW_ENTRY="${NEW_ENTRY} ${EXT_ELEMENT}\n"
- NEW_ENTRY="${NEW_ENTRY} ${EXT_TYPE}\n"
- [ -n "$CLIENT_TAG" ] && NEW_ENTRY="${NEW_ENTRY} ${CLIENT_TAG}\n"
- [ -n "$FOLDER_TAG" ] && NEW_ENTRY="${NEW_ENTRY} ${FOLDER_TAG}\n"
- NEW_ENTRY="${NEW_ENTRY} ${VERSION}\n"
- NEW_ENTRY="${NEW_ENTRY} $(date +%Y-%m-%d)\n"
- NEW_ENTRY="${NEW_ENTRY} https://git.mokoconsulting.tech/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${RELEASE_TAG}\n"
- NEW_ENTRY="${NEW_ENTRY} \n"
- NEW_ENTRY="${NEW_ENTRY} ${DOWNLOAD_URL}\n"
- NEW_ENTRY="${NEW_ENTRY} \n"
- [ -n "$SHA256" ] && NEW_ENTRY="${NEW_ENTRY} ${SHA256}\n"
- NEW_ENTRY="${NEW_ENTRY} ${STABILITY}\n"
- NEW_ENTRY="${NEW_ENTRY} Moko Consulting\n"
- NEW_ENTRY="${NEW_ENTRY} https://mokoconsulting.tech\n"
- NEW_ENTRY="${NEW_ENTRY} \n"
- [ -n "$PHP_MINIMUM" ] && NEW_ENTRY="${NEW_ENTRY} ${PHP_MINIMUM}\n"
- NEW_ENTRY="${NEW_ENTRY} "
-
- # -- Write new entry to temp file --------------------------------
- printf '%b' "$NEW_ENTRY" > /tmp/new_entry.xml
-
- # -- Merge into updates.xml ----------------------------------------
- # Cascade: stable→all | rc→rc+lower | beta→beta+lower | alpha→alpha+dev | dev→dev
- CASCADE_MAP="stable:development,alpha,beta,rc,stable rc:development,alpha,beta,rc beta:development,alpha,beta alpha:development,alpha development:development"
- TARGETS=""
- for entry in $CASCADE_MAP; do
- key="${entry%%:*}"
- vals="${entry#*:}"
- if [ "$key" = "${STABILITY}" ]; then
- TARGETS="$vals"
- break
- fi
- done
- [ -z "$TARGETS" ] && TARGETS="${STABILITY}"
-
- echo "Cascade: ${STABILITY} → ${TARGETS}"
-
- # Create updates.xml if missing
- if [ ! -f "updates.xml" ]; then
- printf '%s\n' "" > updates.xml
- printf '%s\n' "" >> updates.xml
- printf '%s\n' "" >> updates.xml
- printf '%s\n' "" >> updates.xml
- fi
-
- # Update existing blocks or create missing ones
- export PY_TARGETS="$TARGETS" PY_VERSION="$VERSION" PY_DATE="$(date +%Y-%m-%d)"
- python3 << 'PYEOF'
- import re, os
-
- targets = os.environ["PY_TARGETS"].split(",")
- version = os.environ["PY_VERSION"]
- date = os.environ["PY_DATE"]
-
- with open("updates.xml") as f:
- content = f.read()
- with open("/tmp/new_entry.xml") as f:
- new_entry_template = f.read()
-
- for tag in targets:
- tag = tag.strip()
- # Build entry with this tag's name
- new_entry = re.sub(r"[^<]*", f"{tag}", new_entry_template)
-
- # Try to find existing block (handles both single-line and multi-line )
- block_pattern = r"((?:(?!).)*?" + re.escape(tag) + r".*?)"
- match = re.search(block_pattern, content, re.DOTALL)
-
- if match:
- # Update in place — replace entire block
- content = content.replace(match.group(1), new_entry.strip())
- print(f" UPDATED: {tag} → {version}")
- else:
- # Create — insert before
- content = content.replace("", "\n" + new_entry.strip() + "\n\n")
- print(f" CREATED: {tag} → {version}")
-
- # Clean up excessive blank lines
- content = re.sub(r"\n{3,}", "\n\n", content)
-
- with open("updates.xml", "w") as f:
- f.write(content)
- PYEOF
-
- # Commit
- git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
- git config --local user.name "gitea-actions[bot]"
- git add updates.xml
- git diff --cached --quiet || {
- git commit -m "chore: update updates.xml (${STABILITY}: ${DISPLAY_VERSION}) [skip ci]" \
- --author="gitea-actions[bot] "
- git push
- }
-
- # -- Sync updates.xml to main (for non-main branches) ----------------------
- - name: Sync updates.xml to main
- if: github.ref_name != 'main'
- run: |
- API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
- GA_TOKEN="${{ secrets.GA_TOKEN }}"
-
- FILE_SHA=$(curl -sf -H "Authorization: token ${GA_TOKEN}" \
- "${API_BASE}/contents/updates.xml?ref=main" | python3 -c "import sys,json; print(json.load(sys.stdin).get('sha',''))" 2>/dev/null || true)
-
- if [ -n "$FILE_SHA" ] && [ -f "updates.xml" ]; then
- CONTENT=$(base64 -w0 updates.xml)
- curl -sf -X PUT -H "Authorization: token ${GA_TOKEN}" \
- -H "Content-Type: application/json" \
- "${API_BASE}/contents/updates.xml" \
- -d "$(python3 -c "import json; print(json.dumps({
- 'content': '${CONTENT}',
- 'sha': '${FILE_SHA}',
- 'message': 'chore: sync updates.xml from ${STABILITY} [skip ci]',
- 'branch': 'main'
- }))")" > /dev/null 2>&1 \
- && echo "updates.xml synced to main (${STABILITY})" >> $GITHUB_STEP_SUMMARY \
- || echo "WARNING: failed to sync updates.xml to main" >> $GITHUB_STEP_SUMMARY
- else
- echo "WARNING: could not get updates.xml SHA from main" >> $GITHUB_STEP_SUMMARY
- fi
-
- - name: SFTP deploy to dev server
- if: contains(github.ref, 'dev/') || github.ref == 'refs/heads/dev'
- env:
- DEV_HOST: ${{ vars.DEV_FTP_HOST }}
- DEV_PATH: ${{ vars.DEV_FTP_PATH }}
- DEV_SUFFIX: ${{ vars.DEV_FTP_SUFFIX }}
- DEV_USER: ${{ vars.DEV_FTP_USERNAME }}
- DEV_PORT: ${{ vars.DEV_FTP_PORT }}
- DEV_KEY: ${{ secrets.DEV_FTP_KEY }}
- DEV_PASS: ${{ secrets.DEV_FTP_PASSWORD }}
- run: |
- # -- Permission check: admin or maintain role required --------
- ACTOR="${{ github.actor }}"
- REPO="${{ github.repository }}"
- API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
-
- PERMISSION=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
- "${API_BASE}/collaborators/${ACTOR}/permission" 2>/dev/null | \
- python3 -c "import sys,json; print(json.load(sys.stdin).get('permission','read'))" 2>/dev/null || echo "read")
- case "$PERMISSION" in
- admin|maintain|write) ;;
- *)
- echo "Deploy denied: ${ACTOR} has '${PERMISSION}' — requires admin, maintain, or write"
- exit 0
- ;;
- esac
-
- [ -z "$DEV_HOST" ] || [ -z "$DEV_PATH" ] && { echo "DEV FTP not configured — skipping SFTP"; exit 0; }
-
- SOURCE_DIR="src"
- [ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
- [ ! -d "$SOURCE_DIR" ] && exit 0
-
- PORT="${DEV_PORT:-22}"
- REMOTE="${DEV_PATH%/}"
- [ -n "$DEV_SUFFIX" ] && REMOTE="${REMOTE}/${DEV_SUFFIX#/}"
-
- printf '{"host":"%s","port":%s,"username":"%s","remotePath":"%s"' \
- "$DEV_HOST" "$PORT" "$DEV_USER" "$REMOTE" > /tmp/sftp-config.json
- if [ -n "$DEV_KEY" ]; then
- echo "$DEV_KEY" > /tmp/deploy_key && chmod 600 /tmp/deploy_key
- printf ',"privateKeyPath":"/tmp/deploy_key"}' >> /tmp/sftp-config.json
- else
- printf ',"password":"%s"}' "$DEV_PASS" >> /tmp/sftp-config.json
- fi
-
- PLATFORM=$(php /tmp/mokostandards-api/cli/platform_detect.php --path . 2>/dev/null || true)
- if [ "$PLATFORM" = "waas-component" ] && [ -f "/tmp/mokostandards-api/deploy/deploy-joomla.php" ]; then
- php /tmp/mokostandards-api/deploy/deploy-joomla.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json
- elif [ -f "/tmp/mokostandards-api/deploy/deploy-sftp.php" ]; then
- php /tmp/mokostandards-api/deploy/deploy-sftp.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json
- fi
- rm -f /tmp/deploy_key /tmp/sftp-config.json
- echo "SFTP deploy to dev complete" >> $GITHUB_STEP_SUMMARY
-
- - name: Summary
- if: always()
- run: |
- echo "## Joomla Update Server" >> $GITHUB_STEP_SUMMARY
- echo "" >> $GITHUB_STEP_SUMMARY
- echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
- echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
- echo "| Stability | \`${STABILITY}\` |" >> $GITHUB_STEP_SUMMARY
- echo "| Version | \`${DISPLAY_VERSION}\` |" >> $GITHUB_STEP_SUMMARY
- echo "| Element | \`${EXT_ELEMENT}\` |" >> $GITHUB_STEP_SUMMARY
- echo "| Download | [ZIP](${DOWNLOAD_URL}) |" >> $GITHUB_STEP_SUMMARY
diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml
index fc49b37..38eb39a 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: 01.19.00
+# VERSION: 01.20.00
# BRIEF: Auto-create feature branch when an issue is opened
name: "Universal: Issue Branch"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2a0ff35..85606a7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,8 @@
# Changelog
## [Unreleased]
+## [01.20.00] --- 2026-06-04
+
## [01.19.00] --- 2026-06-04
@@ -23,10 +25,3 @@
### Fixed
- Add missing `` section to module manifest
-
-## [01.13] - 2026-06-04
-
-### 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
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 8247207..f4a9f02 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -14,7 +14,7 @@
DEFGROUP:
INGROUP: Project.Documentation
REPO:
- VERSION: 01.19.00
+ VERSION: 01.20.00
PATH: ./CODE_OF_CONDUCT.md
BRIEF: Reference + packaging repo for Moko Consulting Developer GPT Other Default
-->
diff --git a/SECURITY.md b/SECURITY.md
index 8d23d0d..354b7b5 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -23,7 +23,7 @@ DEFGROUP: [PROJECT_NAME]
INGROUP: [PROJECT_NAME].Documentation
REPO: [REPOSITORY_URL]
PATH: /SECURITY.md
-VERSION: 01.19.00
+VERSION: 01.20.00
BRIEF: Security vulnerability reporting and handling policy
-->
diff --git a/src/language/en-GB/mod_mokojoomhero.ini b/src/language/en-GB/mod_mokojoomhero.ini
index 8c8a268..0550515 100644
--- a/src/language/en-GB/mod_mokojoomhero.ini
+++ b/src/language/en-GB/mod_mokojoomhero.ini
@@ -6,7 +6,7 @@
; INGROUP: MokoJoomHero.Module
; REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero
; PATH: /src/language/en-GB/mod_mokojoomhero.ini
-; VERSION: 01.19.00
+; VERSION: 01.20.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."
diff --git a/src/language/en-GB/mod_mokojoomhero.sys.ini b/src/language/en-GB/mod_mokojoomhero.sys.ini
index dde2175..edda0b1 100644
--- a/src/language/en-GB/mod_mokojoomhero.sys.ini
+++ b/src/language/en-GB/mod_mokojoomhero.sys.ini
@@ -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.19.00
+; VERSION: 01.20.00
; BRIEF: System language strings — used in admin Extension Manager and Module Manager
MOD_MOKOJOOMHERO="Module - MokoJoomHero"
diff --git a/src/language/en-US/mod_mokojoomhero.ini b/src/language/en-US/mod_mokojoomhero.ini
index 3f7b473..ad72e48 100644
--- a/src/language/en-US/mod_mokojoomhero.ini
+++ b/src/language/en-US/mod_mokojoomhero.ini
@@ -6,7 +6,7 @@
; INGROUP: MokoJoomHero.Module
; REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero
; PATH: /src/language/en-US/mod_mokojoomhero.ini
-; VERSION: 01.19.00
+; VERSION: 01.20.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."
diff --git a/src/language/en-US/mod_mokojoomhero.sys.ini b/src/language/en-US/mod_mokojoomhero.sys.ini
index 1b45035..b4d815b 100644
--- a/src/language/en-US/mod_mokojoomhero.sys.ini
+++ b/src/language/en-US/mod_mokojoomhero.sys.ini
@@ -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.19.00
+; VERSION: 01.20.00
; BRIEF: System language strings — used in admin Extension Manager and Module Manager (en-US)
MOD_MOKOJOOMHERO="Module - MokoJoomHero"
diff --git a/src/media/css/mod_mokojoomhero.css b/src/media/css/mod_mokojoomhero.css
index 1cd4e98..4648117 100644
--- a/src/media/css/mod_mokojoomhero.css
+++ b/src/media/css/mod_mokojoomhero.css
@@ -7,7 +7,7 @@
* INGROUP: MokoJoomHero.Module
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero
* PATH: /src/media/css/mod_mokojoomhero.css
- * VERSION: 01.19.00
+ * VERSION: 01.20.00
* BRIEF: Hero module stylesheet — slideshow, video, colour/gradient, overlay, card, mute toggle, responsive
*/
diff --git a/src/media/js/mod_mokojoomhero.js b/src/media/js/mod_mokojoomhero.js
index f6a88f6..644bf71 100644
--- a/src/media/js/mod_mokojoomhero.js
+++ b/src/media/js/mod_mokojoomhero.js
@@ -8,7 +8,7 @@
* INGROUP: MokoJoomHero.Module
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero
* PATH: /src/media/js/mod_mokojoomhero.js
- * VERSION: 01.19.00
+ * VERSION: 01.20.00
* BRIEF: Hero module JavaScript — slideshow crossfade, video viewport control, mute toggle
*/
diff --git a/src/mod_mokojoomhero.xml b/src/mod_mokojoomhero.xml
index 035e518..53e9160 100644
--- a/src/mod_mokojoomhero.xml
+++ b/src/mod_mokojoomhero.xml
@@ -22,7 +22,7 @@
https://mokoconsulting.tech
Copyright (C) 2026 Moko Consulting. All rights reserved.
GPL-3.0-or-later
- 01.19.00
+ 01.20.00
Random hero image slideshow, video backgrounds, solid colour/gradient, parallax, content animations, A/B testing, scheduling, and overlay with card support. Free and open source. By Moko Consulting.
script.php