diff --git a/.mokogitea/CLAUDE.md b/.mokogitea/CLAUDE.md
new file mode 100644
index 0000000..1384552
--- /dev/null
+++ b/.mokogitea/CLAUDE.md
@@ -0,0 +1,63 @@
+# MokoOnyx
+
+Joomla site template — successor to MokoCassiopeia. Base template for all WaaS client deployments.
+
+## Quick Reference
+
+| Field | Value |
+|---|---|
+| **Element** | `tpl_mokoonyx` |
+| **Type** | Joomla site template |
+| **Language** | PHP 8.1+ / CSS / JS |
+| **Branch** | develop on `dev`, merge to `main` (protected) |
+| **Wiki** | [MokoOnyx Wiki](https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/wiki) |
+
+## Commands
+
+```bash
+make build # Build template ZIP
+make lint # Run linters
+make validate # Validate structure
+make release # Full release pipeline
+make minify # Minify CSS/JS assets
+make clean # Clean build artifacts
+composer install # Install PHP dependencies
+```
+
+## Architecture
+
+Joomla **site template** — the base layer that client theme packages override:
+
+- `src/templateDetails.xml` — template manifest
+- `src/index.php` — main template entry point
+- `src/error.php` — error page
+- `src/offline.php` — maintenance page
+- `src/component.php` — print/component-only layout
+- `src/html/` — template overrides for core components
+- `src/media/css/` — base stylesheets
+- `src/media/js/` — base scripts
+- `src/media/images/` — template images
+- `src/language/` — translations
+
+### Client Theme Packages
+
+Client repos (`client-clarksvillefurs`, `client-optainfunding`, etc.) install `type="file"` packages that overlay client-specific CSS, images, and JS into the MokoOnyx media directory. MokoOnyx provides the structure; client themes customize the appearance.
+
+### Minification
+
+`MokoMinifyHelper` handles runtime CSS/JS minification in Joomla. Build-time minification via `make minify`. Never commit `*.min.css`/`*.min.js` — they're generated.
+
+## Rules
+
+- **Never commit** `.claude/`, `.mcp.json`, `TODO.md`, `*.min.css`/`*.min.js`
+- **Attribution**: `Authored-by: Moko Consulting`
+- **Workflow directory**: `.mokogitea/` (not `.gitea/` or `.github/`)
+- **Minification**: handled at build time (CI) and runtime (MokoMinifyHelper)
+- **Wiki**: documentation lives in the Gitea wiki, not `docs/` files
+- **Standards**: [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/moko-platform/wiki/Home)
+
+## Coding Standards
+
+- PHP 8.1+ minimum
+- SPDX license headers on all PHP files
+- `defined('_JEXEC') or die;` on all PHP files
diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml
index e134379..d7e107c 100644
--- a/.mokogitea/manifest.xml
+++ b/.mokogitea/manifest.xml
@@ -9,7 +9,7 @@
Template - MokoOnyx
MokoConsulting
MokoOnyx - Joomla site template (successor to MokoCassiopeia)
- 02.20.00
+ 02.19.07
GNU General Public License v3
diff --git a/.mokogitea/workflows/auto-release.yml b/.mokogitea/workflows/auto-release.yml
index 44a2d64..ca40435 100644
--- a/.mokogitea/workflows/auto-release.yml
+++ b/.mokogitea/workflows/auto-release.yml
@@ -17,7 +17,7 @@
# | Reads manifest.xml (joomla|dolibarr|generic) to branch logic. |
# | |
# | Platform-specific: |
-# | joomla: XML manifest, updates.xml, type-prefixed packages |
+# | joomla: XML manifest, type-prefixed packages |
# | dolibarr: mod*.class.php, update.txt, dev version reset |
# | generic: README-only, no update stream |
# | |
@@ -71,20 +71,25 @@ jobs:
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
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
+ if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then
+ echo Using pre-installed /opt/moko-platform
+ echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV
+ else
+ echo Falling back to fresh clone
+ if ! command -v composer > /dev/null 2>&1; 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
+ rm -rf /tmp/moko-platform-api
+ CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git
+ git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api
+ cd /tmp/moko-platform-api
+ composer install --no-dev --no-interaction --quiet
+ echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV
fi
- # Always fetch latest CLI tools — never use stale cache from previous runs
- rm -rf /tmp/moko-platform-api
- git clone --depth 1 --branch main --quiet \
- "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
- /tmp/moko-platform-api
- cd /tmp/moko-platform-api
- composer install --no-dev --no-interaction --quiet
- name: Rename branch to rc
run: |
- php /tmp/moko-platform-api/cli/branch_rename.php \
+ php ${MOKO_CLI}/branch_rename.php \
--from "${{ github.event.pull_request.head.ref || 'dev' }}" --to rc \
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
--api-base "${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" \
@@ -100,16 +105,15 @@ jobs:
- name: Publish RC release
run: |
- php /tmp/moko-platform-api/cli/release_publish.php \
+ php ${MOKO_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 release built" >> $GITHUB_STEP_SUMMARY
# ── Merged PR → Build & Release (or promote RC to stable) ────────────────────
release:
@@ -151,25 +155,60 @@ jobs:
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}'
run: |
- # Ensure PHP + Composer are available
- 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
+ if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then
+ echo Using pre-installed /opt/moko-platform
+ echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV
+ else
+ echo Falling back to fresh clone
+ if ! command -v composer > /dev/null 2>&1; 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
+ rm -rf /tmp/moko-platform-api
+ CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git
+ git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api
+ cd /tmp/moko-platform-api
+ composer install --no-dev --no-interaction --quiet
+ echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV
fi
- # Always fetch latest CLI tools — never use stale cache from previous runs
- rm -rf /tmp/moko-platform-api
- git clone --depth 1 --branch main --quiet \
- "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
- /tmp/moko-platform-api
- cd /tmp/moko-platform-api
- composer install --no-dev --no-interaction --quiet
-
- name: "Publish stable release"
run: |
- php /tmp/moko-platform-api/cli/release_publish.php \
+ php ${MOKO_CLI}/release_publish.php \
--path . --stability stable --bump minor --branch main \
- --token "${{ secrets.MOKOGITEA_TOKEN }}" \
- --skip-update-stream
+ --token "${{ secrets.MOKOGITEA_TOKEN }}"
+
+ - name: Update release notes from CHANGELOG.md
+ run: |
+ API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
+
+ # Extract [Unreleased] section from changelog
+ if [ -f "CHANGELOG.md" ]; then
+ NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md)
+ [ -z "$NOTES" ] && NOTES="Stable release"
+ else
+ NOTES="Stable release"
+ fi
+
+ # Update release body via API
+ RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
+ "${API_BASE}/releases/tags/stable" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
+
+ if [ -n "$RELEASE_ID" ]; then
+ python3 -c "
+ import json, urllib.request
+ body = open('/dev/stdin').read()
+ payload = json.dumps({'body': body}).encode()
+ req = urllib.request.Request(
+ '${API_BASE}/releases/${RELEASE_ID}',
+ data=payload, method='PATCH',
+ headers={
+ 'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}',
+ 'Content-Type': 'application/json'
+ })
+ urllib.request.urlopen(req)
+ " <<< "$NOTES"
+ echo "Release notes updated from CHANGELOG.md"
+ fi
# -- STEP 9: Mirror to GitHub (stable only) --------------------------------
- name: "Step 9: Mirror release to GitHub"
@@ -182,7 +221,7 @@ jobs:
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
- php /tmp/moko-platform-api/cli/release_mirror.php \
+ php ${MOKO_CLI}/release_mirror.php \
--version "$VERSION" --tag "$RELEASE_TAG" \
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
--gh-token "${{ secrets.GH_MIRROR_TOKEN }}" --gh-repo "$GH_REPO" \
@@ -256,7 +295,7 @@ jobs:
continue-on-error: true
run: |
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
- php /tmp/moko-platform-api/cli/version_reset_dev.php \
+ php ${MOKO_CLI}/version_reset_dev.php \
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \
--branch dev --path . 2>&1 || true
diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml
index 5e03998..3a44f6a 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: 02.20.00
+# VERSION: 02.19.07
# BRIEF: Auto-create feature branch when an issue is opened
name: "Universal: Issue Branch"
diff --git a/.mokogitea/workflows/pre-release.yml b/.mokogitea/workflows/pre-release.yml
index 780150d..9615a4e 100644
--- a/.mokogitea/workflows/pre-release.yml
+++ b/.mokogitea/workflows/pre-release.yml
@@ -1,259 +1,243 @@
-# Copyright (C) 2026 Moko Consulting
-#
-# SPDX-License-Identifier: GPL-3.0-or-later
-#
-# FILE INFORMATION
-# DEFGROUP: Gitea.Workflow
-# INGROUP: moko-platform.Release
-# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
-# PATH: /templates/workflows/universal/pre-release.yml.template
-# VERSION: 05.01.00
-# BRIEF: Manual pre-release -- builds dev/alpha/beta/rc packages from any branch
-
-name: "Universal: Pre-Release"
-
-on:
- pull_request:
- types: [closed]
- branches:
- - dev
- pull_request_target:
- types: [synchronize, opened, reopened]
- branches:
- - main
- workflow_dispatch:
- inputs:
- stability:
- description: 'Pre-release channel'
- required: true
- type: choice
- options:
- - development
- - alpha
- - beta
- - release-candidate
-
-permissions:
- contents: write
-
-env:
- 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 }}
-
-jobs:
- build:
- name: "Build Pre-Release (${{ inputs.stability || 'development' }})"
- runs-on: release
- if: >-
- github.event_name == 'workflow_dispatch' ||
- (github.event_name == 'pull_request' && github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev') ||
- (github.event_name == 'pull_request_target' && github.event.pull_request.base.ref == 'main')
-
- steps:
- - name: Checkout
- uses: actions/checkout@v4
- with:
- fetch-depth: 0
- token: ${{ secrets.MOKOGITEA_TOKEN }}
- ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || '' }}
-
- - name: Setup moko-platform tools
- env:
- MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
- MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
- 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
- # Always fetch latest CLI tools — never use stale cache from previous runs
- rm -rf /tmp/moko-platform-api
- git clone --depth 1 --branch main --quiet \
- "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
- /tmp/moko-platform-api
- cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet
- echo "MOKO_CLI=/tmp/moko-platform-api/cli" >> "$GITHUB_ENV"
-
- - name: Detect platform
- id: platform
- run: |
- php ${MOKO_CLI}/manifest_read.php --path . --github-output
-
- - name: Resolve metadata and bump version
- id: meta
- run: |
- # Auto-detect stability: RC for PRs targeting main, else use input or default to development
- if [ "${{ github.event_name }}" = "pull_request_target" ] && [ "${{ github.event.pull_request.base.ref }}" = "main" ]; then
- STABILITY="release-candidate"
- else
- STABILITY="${{ inputs.stability || 'development' }}"
- fi
-
- case "$STABILITY" in
- development) SUFFIX="-dev"; TAG="development" ;;
- alpha) SUFFIX="-alpha"; TAG="alpha" ;;
- beta) SUFFIX="-beta"; TAG="beta" ;;
- release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;;
- esac
-
- # Read current version (bump already handled by push workflow)
- VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null)
- [ -z "$VERSION" ] && VERSION="00.00.01"
-
- # Strip any existing suffix from version before applying stability
- VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//')
-
- php ${MOKO_CLI}/version_set_platform.php \
- --path . --version "$VERSION" --branch "${{ github.ref_name }}" --stability "$STABILITY" 2>/dev/null || true
-
- # Verify version consistency across all files
- php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
-
- # Update VERSION variable with suffix
- if [ -n "$SUFFIX" ]; then
- VERSION="${VERSION}${SUFFIX}"
- fi
-
- # Commit version bump
- git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
- git config --local user.name "gitea-actions[bot]"
- git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
- git add -A
- git diff --cached --quiet || {
- git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]"
- git push origin HEAD 2>&1
- }
-
- # Auto-detect element via manifest_element.php
- php ${MOKO_CLI}/manifest_element.php \
- --path . --version "$VERSION" --stability "$STABILITY" \
- --repo "${GITEA_REPO}" --github-output
-
- # Read back element outputs
- EXT_ELEMENT=$(grep '^ext_element=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
- ZIP_NAME=$(grep '^zip_name=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
- [ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
- [ -z "$ZIP_NAME" ] && ZIP_NAME="${EXT_ELEMENT}-${VERSION}.zip"
-
- echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
- echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT"
- echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT"
- echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
- echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT"
- echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT"
-
- echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ==="
-
- - name: Create release
- id: release
- run: |
- TAG="${{ steps.meta.outputs.tag }}"
- VERSION="${{ steps.meta.outputs.version }}"
- API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
- php ${MOKO_CLI}/release_create.php \
- --path . --version "$VERSION" --tag "$TAG" \
- --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
- --repo "${GITEA_REPO}" --branch dev --prerelease
-
- - name: Ensure prerelease flag
- run: |
- TAG="${{ steps.meta.outputs.tag }}"
- API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
- # Get release ID by tag and force prerelease=true
- RELEASE_ID=$(curl -s "${API_BASE}/releases/tags/${TAG}" \
- -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" | jq -r '.id // empty')
- if [ -n "$RELEASE_ID" ]; then
- curl -s -X PATCH "${API_BASE}/releases/${RELEASE_ID}" \
- -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
- -H "Content-Type: application/json" \
- -d '{"prerelease": true}'
- echo "Marked release ${TAG} (id=${RELEASE_ID}) as prerelease"
- fi
-
- - name: Build package and upload
- id: package
- run: |
- VERSION="${{ steps.meta.outputs.version }}"
- TAG="${{ steps.meta.outputs.tag }}"
- API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
- php ${MOKO_CLI}/release_package.php \
- --path . --version "$VERSION" --tag "$TAG" \
- --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
- --repo "${GITEA_REPO}" --output /tmp || true
-
- - name: Update updates.xml
- if: steps.platform.outputs.platform == 'joomla'
- run: |
- VERSION="${{ steps.meta.outputs.version }}"
- STABILITY="${{ steps.meta.outputs.stability }}"
- SHA256="${{ steps.package.outputs.sha256_zip }}"
-
- if [ ! -f "updates.xml" ]; then
- echo "No updates.xml -- skipping"
- exit 0
- fi
-
- SHA_FLAG=""
- [ -n "$SHA256" ] && SHA_FLAG="--sha ${SHA256}"
-
- php ${MOKO_CLI}/updates_xml_build.php \
- --path . --version "${VERSION}" --stability "${STABILITY}" \
- --gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \
- ${SHA_FLAG}
-
- # Commit and push
- if ! git diff --quiet updates.xml 2>/dev/null; then
- git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
- git config --local user.name "gitea-actions[bot]"
- git add updates.xml
- git commit -m "chore: update ${STABILITY} channel ${VERSION} [skip ci]"
- git push origin HEAD 2>&1 || echo "WARNING: push failed"
- fi
-
- - name: "Sync updates.xml to all branches"
- if: steps.platform.outputs.platform == 'joomla'
- run: |
- CURRENT_BRANCH="${{ github.ref_name }}"
- git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
- git config --local user.name "gitea-actions[bot]"
-
- for BRANCH in main dev; do
- [ "$BRANCH" = "$CURRENT_BRANCH" ] && continue
- echo "Syncing updates.xml -> ${BRANCH}"
- git fetch origin "${BRANCH}" 2>/dev/null || continue
- git checkout "origin/${BRANCH}" -- updates.xml 2>/dev/null || continue
- git checkout "${CURRENT_BRANCH}" -- updates.xml
- if ! git diff --quiet updates.xml 2>/dev/null; then
- git add updates.xml
- git commit -m "chore: sync updates.xml from ${CURRENT_BRANCH} [skip ci]"
- git push origin HEAD:refs/heads/${BRANCH} 2>&1 || echo "WARNING: push to ${BRANCH} failed"
- fi
- git checkout "${CURRENT_BRANCH}" 2>/dev/null
- done
-
- - name: "Delete lesser pre-release channels (cascade)"
- continue-on-error: true
- run: |
- API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
- TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
-
- php ${MOKO_CLI}/release_cascade.php \
- --stability "${{ steps.meta.outputs.stability }}" \
- --token "${TOKEN}" \
- --api-base "${API_BASE}"
-
- - name: Summary
- if: always()
- run: |
- VERSION="${{ steps.meta.outputs.version }}"
- STABILITY="${{ steps.meta.outputs.stability }}"
- ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
- SHA256="${{ steps.package.outputs.sha256_zip }}"
- echo "## Pre-Release Complete" >> $GITHUB_STEP_SUMMARY
- echo "" >> $GITHUB_STEP_SUMMARY
- echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
- echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
- echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
- echo "| Channel | ${STABILITY} |" >> $GITHUB_STEP_SUMMARY
- echo "| Package | \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY
- echo "| SHA-256 | \`${SHA256:-n/a}\` |" >> $GITHUB_STEP_SUMMARY
+# Copyright (C) 2026 Moko Consulting
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# FILE INFORMATION
+# DEFGROUP: Gitea.Workflow
+# INGROUP: moko-platform.Release
+# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
+# PATH: /templates/workflows/universal/pre-release.yml.template
+# VERSION: 05.01.00
+# BRIEF: Manual pre-release -- builds dev/alpha/beta/rc packages from any branch
+
+name: "Universal: Pre-Release"
+
+on:
+ pull_request:
+ types: [closed]
+ branches:
+ - dev
+ pull_request_target:
+ types: [synchronize, opened, reopened]
+ branches:
+ - main
+ workflow_dispatch:
+ inputs:
+ stability:
+ description: 'Pre-release channel'
+ required: true
+ type: choice
+ options:
+ - development
+ - alpha
+ - beta
+ - release-candidate
+
+permissions:
+ contents: write
+
+env:
+ 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 }}
+
+jobs:
+ build:
+ name: "Build Pre-Release (${{ inputs.stability || 'development' }})"
+ runs-on: release
+ if: >-
+ github.event_name == 'workflow_dispatch' ||
+ (github.event_name == 'pull_request' && github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev') ||
+ (github.event_name == 'pull_request_target' && github.event.pull_request.base.ref == 'main')
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ token: ${{ secrets.MOKOGITEA_TOKEN }}
+ ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || '' }}
+
+ - name: Setup moko-platform tools
+ env:
+ MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
+ MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
+ run: |
+ # Use pre-installed /opt/moko-platform if available (updated by cron every 6h)
+ if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/cli/manifest_element.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then
+ echo Using pre-installed /opt/moko-platform
+ echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV
+ else
+ echo Falling back to fresh clone
+ if ! command -v composer > /dev/null 2>&1; 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
+ rm -rf /tmp/moko-platform-api
+ CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git
+ git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api
+ cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet
+ echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV
+ fi
+
+ - name: Detect platform
+ id: platform
+ run: |
+ php ${MOKO_CLI}/manifest_read.php --path . --github-output
+
+ - name: Resolve metadata and bump version
+ id: meta
+ run: |
+ # Auto-detect stability: RC for PRs targeting main, else use input or default to development
+ if [ "${{ github.event_name }}" = "pull_request_target" ] && [ "${{ github.event.pull_request.base.ref }}" = "main" ]; then
+ STABILITY="release-candidate"
+ else
+ STABILITY="${{ inputs.stability || 'development' }}"
+ fi
+
+ case "$STABILITY" in
+ development) SUFFIX="-dev"; TAG="development" ;;
+ alpha) SUFFIX="-alpha"; TAG="alpha" ;;
+ beta) SUFFIX="-beta"; TAG="beta" ;;
+ release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;;
+ esac
+
+ # Bump version via CLI: patch for dev/alpha/beta, minor for RC
+ case "$STABILITY" in
+ release-candidate) BUMP="minor" ;;
+ *) BUMP="patch" ;;
+ esac
+
+ php ${MOKO_CLI}/version_bump.php --path . $([ "$BUMP" = "minor" ] && echo "--minor") 2>/dev/null || true
+
+ # Set stability suffix and verify consistency
+ VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "00.00.01")
+ VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//')
+
+ php ${MOKO_CLI}/version_set_platform.php \
+ --path . --version "$VERSION" --branch "${{ github.ref_name }}" --stability "$STABILITY" 2>/dev/null || true
+ php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
+
+ # Ensure licensing tags (updateservers, dlid) if enabled in manifest.xml
+ php ${MOKO_CLI}/manifest_licensing.php --path . --fix 2>/dev/null || true
+
+ # Append suffix for output
+ if [ -n "$SUFFIX" ]; then
+ VERSION="${VERSION}${SUFFIX}"
+ fi
+
+ # Commit version bump
+ git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
+ git config --local user.name "gitea-actions[bot]"
+ git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
+ git add -A
+ git diff --cached --quiet || {
+ git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]"
+ git push origin HEAD 2>&1
+ }
+
+ # Auto-detect element via manifest_element.php
+ php ${MOKO_CLI}/manifest_element.php \
+ --path . --version "$VERSION" --stability "$STABILITY" \
+ --repo "${GITEA_REPO}" --github-output
+
+ # Read back element outputs
+ EXT_ELEMENT=$(grep '^ext_element=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
+ ZIP_NAME=$(grep '^zip_name=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
+ [ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
+ [ -z "$ZIP_NAME" ] && ZIP_NAME="${EXT_ELEMENT}-${VERSION}.zip"
+
+ echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
+ echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT"
+ echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT"
+ echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
+ echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT"
+ echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT"
+
+ echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ==="
+
+ - name: Create release
+ id: release
+ run: |
+ TAG="${{ steps.meta.outputs.tag }}"
+ VERSION="${{ steps.meta.outputs.version }}"
+ API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
+ php ${MOKO_CLI}/release_create.php \
+ --path . --version "$VERSION" --tag "$TAG" \
+ --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
+ --repo "${GITEA_REPO}" --branch dev --prerelease
+
+ - name: Update release notes from CHANGELOG.md
+ run: |
+ TAG="${{ steps.meta.outputs.tag }}"
+ VERSION="${{ steps.meta.outputs.version }}"
+ API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
+
+ # Extract [Unreleased] section from changelog (everything between [Unreleased] and next ## heading)
+ if [ -f "CHANGELOG.md" ]; then
+ NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md)
+ [ -z "$NOTES" ] && NOTES="Release ${VERSION}"
+ else
+ NOTES="Release ${VERSION}"
+ fi
+
+ # Update release body via API
+ RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
+ "${API_BASE}/releases/tags/${TAG}" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
+
+ if [ -n "$RELEASE_ID" ]; then
+ python3 -c "
+ import json, urllib.request
+ body = open('/dev/stdin').read()
+ payload = json.dumps({'body': body}).encode()
+ req = urllib.request.Request(
+ '${API_BASE}/releases/${RELEASE_ID}',
+ data=payload, method='PATCH',
+ headers={
+ 'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}',
+ 'Content-Type': 'application/json'
+ })
+ urllib.request.urlopen(req)
+ " <<< "$NOTES"
+ echo "Release notes updated from CHANGELOG.md"
+ fi
+
+ - name: Build package and upload
+ id: package
+ run: |
+ VERSION="${{ steps.meta.outputs.version }}"
+ TAG="${{ steps.meta.outputs.tag }}"
+ API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
+ php ${MOKO_CLI}/release_package.php \
+ --path . --version "$VERSION" --tag "$TAG" \
+ --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
+ --repo "${GITEA_REPO}" --output /tmp || true
+
+ # updates.xml is generated dynamically by MokoGitea license server
+ # No need to build, commit, or sync updates.xml from workflows
+
+ - name: "Delete lesser pre-release channels (cascade)"
+ continue-on-error: true
+ run: |
+ API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
+ TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
+
+ php ${MOKO_CLI}/release_cascade.php \
+ --stability "${{ steps.meta.outputs.stability }}" \
+ --token "${TOKEN}" \
+ --api-base "${API_BASE}"
+
+ - name: Summary
+ if: always()
+ run: |
+ VERSION="${{ steps.meta.outputs.version }}"
+ STABILITY="${{ steps.meta.outputs.stability }}"
+ ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
+ SHA256="${{ steps.package.outputs.sha256_zip }}"
+ echo "## Pre-Release Complete" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
+ echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
+ echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
+ echo "| Channel | ${STABILITY} |" >> $GITHUB_STEP_SUMMARY
+ echo "| Package | \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY
+ echo "| SHA-256 | \`${SHA256:-n/a}\` |" >> $GITHUB_STEP_SUMMARY
diff --git a/.mokogitea/workflows/update-submodule-waas.yml b/.mokogitea/workflows/update-submodule-waas.yml
deleted file mode 100644
index 39461ce..0000000
--- a/.mokogitea/workflows/update-submodule-waas.yml
+++ /dev/null
@@ -1,106 +0,0 @@
-# Copyright (C) 2026 Moko Consulting
-#
-# SPDX-License-Identifier: GPL-3.0-or-later
-#
-# BRIEF: Update MokoOnyx submodule in MokoWaas on main and dev branches
-
-name: "Update MokoWaas Submodule"
-
-on:
- release:
- types: [published]
-
-env:
- FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
- GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
- WAAS_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
- WAAS_REPO: MokoWaaS
- # Path where MokoOnyx lives as a submodule inside MokoWaaS
- SUBMODULE_PATH: src/packages/tpl_mokoonyx
-
-permissions:
- contents: read
-
-jobs:
- update-submodule:
- name: "Update submodule on ${{ matrix.branch }}"
- runs-on: ubuntu-latest
- strategy:
- matrix:
- branch: [main, dev]
- fail-fast: false
-
- steps:
- - name: Get release info
- id: release
- run: |
- TAG="${{ github.event.release.tag_name }}"
- IS_PRE="${{ github.event.release.prerelease }}"
- echo "tag=${TAG}" >> $GITHUB_OUTPUT
- echo "prerelease=${IS_PRE}" >> $GITHUB_OUTPUT
- echo "Release tag: ${TAG} (prerelease: ${IS_PRE})"
-
- # Skip pre-releases (RC) — only update on stable releases
- - name: Skip pre-releases
- if: steps.release.outputs.prerelease == 'true'
- run: |
- echo "Skipping pre-release ${TAG} — only stable releases update MokoWaas"
- exit 0
-
- - name: Clone MokoWaas
- if: steps.release.outputs.prerelease != 'true'
- run: |
- git clone --branch "${{ matrix.branch }}" --depth 1 --recurse-submodules \
- "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${WAAS_ORG}/${WAAS_REPO}.git" \
- waas
- env:
- GIT_TERMINAL_PROMPT: 0
-
- - name: Configure git
- if: steps.release.outputs.prerelease != 'true'
- working-directory: waas
- run: |
- git config user.email "gitea-actions[bot]@mokoconsulting.tech"
- git config user.name "gitea-actions[bot]"
- git remote set-url origin \
- "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${WAAS_ORG}/${WAAS_REPO}.git"
-
- - name: Update submodule to release tag
- if: steps.release.outputs.prerelease != 'true'
- working-directory: waas
- run: |
- TAG="${{ steps.release.outputs.tag }}"
- SUBPATH="${SUBMODULE_PATH}"
-
- # Verify submodule exists
- if [ ! -f ".gitmodules" ] || ! grep -q "${SUBPATH}" .gitmodules; then
- echo "::error::Submodule '${SUBPATH}' not found in .gitmodules on ${{ matrix.branch }}"
- exit 1
- fi
-
- # Fetch the tag in the submodule and update to it
- cd "${SUBPATH}"
- git fetch origin "refs/tags/${TAG}:refs/tags/${TAG}" --depth 1
- git checkout "${TAG}"
- cd -
-
- # Stage the submodule pointer change
- git add "${SUBPATH}"
-
- # Only commit if there's actually a change
- if git diff --cached --quiet; then
- echo "Submodule already at ${TAG} on ${{ matrix.branch }} — nothing to do"
- else
- git commit -m "chore: update MokoOnyx submodule to ${TAG}
-
-Authored-by: Moko Consulting"
- git push origin "${{ matrix.branch }}"
- echo "Updated MokoOnyx to ${TAG} on ${{ matrix.branch }}"
- fi
-
- - name: Summary
- if: always() && steps.release.outputs.prerelease != 'true'
- run: |
- TAG="${{ steps.release.outputs.tag }}"
- echo "## MokoWaas Submodule Update (${{ matrix.branch }})" >> $GITHUB_STEP_SUMMARY
- echo "Updated \`${SUBMODULE_PATH}\` to \`${TAG}\`" >> $GITHUB_STEP_SUMMARY
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4561fb2..ce16c15 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,16 +8,13 @@
DEFGROUP: Joomla.Template.Site
INGROUP: MokoOnyx.Documentation
PATH: ./CHANGELOG.md
- VERSION: 02.20.00
+ VERSION: 02.19.07
BRIEF: Changelog file documenting version history of MokoOnyx
-->
-# Changelog — MokoOnyx (VERSION: 02.20.00)
-
+# Changelog — MokoOnyx (VERSION: 02.19.07)
## [Unreleased]
-## [02.20.00] --- 2026-06-04
-
### Fixed
- Strip Joomla-injected `p-2` padding class from Font Awesome icons in all menu overrides (default, mainmenu, horizontal)
diff --git a/CLAUDE.md b/CLAUDE.md
deleted file mode 100644
index 6bf367c..0000000
--- a/CLAUDE.md
+++ /dev/null
@@ -1,42 +0,0 @@
-# CLAUDE.md
-
-This file provides guidance to Claude Code when working with this repository.
-
-## Project Overview
-
-**MokoOnyx** -- MokoOnyx - Joomla site template (successor to MokoCassiopeia)
-
-| Field | Value |
-|---|---|
-| **Platform** | joomla |
-| **Language** | PHP |
-| **Default branch** | main |
-| **License** | GPL-3.0-or-later |
-| **Wiki** | [MokoOnyx Wiki](https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/wiki) |
-| **Standards** | [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/moko-platform/wiki/Home) |
-
-## Common Commands
-
-```bash
-composer install # Install PHP dependencies
-```
-
-## Architecture
-
-This is a Joomla extension. Key directories:
-- `src/` -- extension source (deployed to Joomla)
-- `src/*.xml` -- manifest file (version, files, params)
-- `src/src/` or `src/services/` -- PHP classes
-- `src/language/` -- translation strings
-- `src/media/` -- CSS/JS/images
-
-## Rules
-
-- **Workflow directory**: `.mokogitea/` (not `.gitea/` or `.github/`)
-
-- **Never commit** `.claude/`, `.mcp.json`, `TODO.md`, or `*.min.css`/`*.min.js`
-- **Attribution**: use `Authored-by: Moko Consulting` in commits
-- **Branch strategy**: develop on `dev`, merge to `main` for release
-- **Minification**: handled at build time (CI) and runtime (MokoMinifyHelper for Joomla templates)
-- **Wiki**: documentation lives in the Gitea wiki, not in `docs/` files
-- **Standards**: this repo follows [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/moko-platform/wiki/Home)
diff --git a/SECURITY.md b/SECURITY.md
index 46cba7c..343ccbc 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -10,7 +10,7 @@
INGROUP: MokoOnyx.Governance
REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx
FILE: SECURITY.md
- VERSION: 02.20.00
+ VERSION: 02.19.07
BRIEF: Security policy and vulnerability reporting process for MokoOnyx.
PATH: /SECURITY.md
NOTE: This policy is process oriented and does not replace secure engineering practices.
diff --git a/src/helper/migrate.php b/src/helper/migrate.php
index b57d956..1c9e088 100644
--- a/src/helper/migrate.php
+++ b/src/helper/migrate.php
@@ -236,11 +236,11 @@ use Joomla\CMS\Log\Log;
// Update the update server
try {
- $onyxUpdatesUrl = 'https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/raw/branch/main/updates.xml';
+ $onyxUpdatesUrl = 'https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/updates.xml';
$query = $db->getQuery(true)
->update('#__update_sites')
->set($db->quoteName('location') . ' = ' . $db->quote($onyxUpdatesUrl))
- ->set($db->quoteName('name') . ' = ' . $db->quote($newDisplay))
+ ->set($db->quoteName('name') . ' = ' . $db->quote('Template - MokoOnyx'))
->where($db->quoteName('location') . ' LIKE ' . $db->quote('%MokoCassiopeia%'));
$db->setQuery($query)->execute();
$n = $db->getAffectedRows();
diff --git a/src/html/layouts/joomla/module/card.php b/src/html/layouts/joomla/module/card.php
index 316554d..0c1a69c 100644
--- a/src/html/layouts/joomla/module/card.php
+++ b/src/html/layouts/joomla/module/card.php
@@ -10,7 +10,7 @@
* INGROUP: MokoOnyx
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx
* PATH: /html/layouts/joomla/module/card.php
- * VERSION: 02.20.00
+ * VERSION: 02.19.07
* BRIEF: Custom card module chrome — renders module titles for all modules
*/
diff --git a/src/html/layouts/mokoonyx/article-metadata.php b/src/html/layouts/mokoonyx/article-metadata.php
index 17812e1..e80034c 100644
--- a/src/html/layouts/mokoonyx/article-metadata.php
+++ b/src/html/layouts/mokoonyx/article-metadata.php
@@ -11,7 +11,7 @@
* INGROUP: MokoOnyx.Layouts
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx
* PATH: /src/html/layouts/mokoonyx/article-metadata.php
- * VERSION: 02.20.00
+ * VERSION: 02.19.07
* BRIEF: Article metadata footer layout -- renders jcfields grouped by field group
*/
diff --git a/src/media/css/a11y-high-contrast.css b/src/media/css/a11y-high-contrast.css
index e9e2471..2bd790d 100644
--- a/src/media/css/a11y-high-contrast.css
+++ b/src/media/css/a11y-high-contrast.css
@@ -10,7 +10,7 @@
* INGROUP: MokoOnyx.Accessibility
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx
* PATH: ./media/css/a11y-high-contrast.css
- * VERSION: 02.20.00
+ * VERSION: 02.19.07
* BRIEF: High-contrast stylesheet for accessibility toolbar
*/
diff --git a/src/script.php b/src/script.php
index 35f6641..5496c17 100644
--- a/src/script.php
+++ b/src/script.php
@@ -34,6 +34,9 @@ class Tpl_MokoonyxInstallerScript implements InstallerScriptInterface
private const OLD_DISPLAY = 'MokoCassiopeia';
private const NEW_DISPLAY = 'MokoOnyx';
+
+
+
public function preflight(string $type, InstallerAdapter $parent): bool
{
if (version_compare(PHP_VERSION, self::MIN_PHP, '<')) {
@@ -83,11 +86,16 @@ class Tpl_MokoonyxInstallerScript implements InstallerScriptInterface
public function uninstall(InstallerAdapter $parent): bool
{
$this->logMessage('MokoOnyx template uninstalled.');
+ $this->saveDownloadKey();
+
return true;
}
public function postflight(string $type, InstallerAdapter $parent): bool
{
+ $this->restoreDownloadKey();
+ $this->warnMissingLicenseKey();
+
if ($type === 'install' || $type === 'update') {
$this->migrateFromCassiopeia();
$this->replaceCassiopeiaReferences();
@@ -533,6 +541,37 @@ class Tpl_MokoonyxInstallerScript implements InstallerScriptInterface
}
}
+ // Remove faulty "template-mokoonyx" duplicate (wrong element name from bad install)
+ $faultyId = (int) $db->setQuery(
+ $db->getQuery(true)
+ ->select('extension_id')
+ ->from('#__extensions')
+ ->where($db->quoteName('element') . ' = ' . $db->quote('template-mokoonyx'))
+ ->where($db->quoteName('type') . ' = ' . $db->quote('template'))
+ )->loadResult();
+
+ if ($faultyId) {
+ $db->setQuery(
+ $db->getQuery(true)
+ ->delete('#__update_sites_extensions')
+ ->where('extension_id = ' . $faultyId)
+ )->execute();
+
+ $db->setQuery(
+ $db->getQuery(true)
+ ->delete('#__extensions')
+ ->where('extension_id = ' . $faultyId)
+ )->execute();
+
+ $db->setQuery(
+ $db->getQuery(true)
+ ->delete('#__template_styles')
+ ->where($db->quoteName('template') . ' = ' . $db->quote('template-mokoonyx'))
+ )->execute();
+
+ $this->logMessage('Removed faulty template-mokoonyx duplicate extension.');
+ }
+
// Remove stale MokoCassiopeia if not set as default
$oldExt = (int) $db->setQuery(
$db->getQuery(true)
@@ -653,4 +692,92 @@ class Tpl_MokoonyxInstallerScript implements InstallerScriptInterface
Log::add($message, $priorities[$priority] ?? Log::INFO, 'mokoonyx');
}
+
+
+ private ?string $savedDownloadKey = null;
+
+ private function saveDownloadKey(): void
+ {
+ try
+ {
+ $db = \Joomla\CMS\Factory::getDbo();
+ $db->setQuery(
+ $db->getQuery(true)
+ ->select($db->quoteName('us.extra_query'))
+ ->from($db->quoteName('#__update_sites', 'us'))
+ ->join('INNER', $db->quoteName('#__update_sites_extensions', 'use') . ' ON use.update_site_id = us.update_site_id')
+ ->join('INNER', $db->quoteName('#__extensions', 'e') . ' ON e.extension_id = use.extension_id')
+ ->where($db->quoteName('e.element') . ' = ' . $db->quote('mokoonyx'))
+ ->setLimit(1)
+ );
+ $key = $db->loadResult();
+ if (!empty($key)) { $this->savedDownloadKey = $key; }
+ }
+ catch (\Throwable $e) {}
+ }
+
+ private function restoreDownloadKey(): void
+ {
+ if ($this->savedDownloadKey === null) { return; }
+
+ try
+ {
+ $db = \Joomla\CMS\Factory::getDbo();
+ $db->setQuery(
+ $db->getQuery(true)
+ ->select($db->quoteName('us.update_site_id'))
+ ->from($db->quoteName('#__update_sites', 'us'))
+ ->join('INNER', $db->quoteName('#__update_sites_extensions', 'use') . ' ON use.update_site_id = us.update_site_id')
+ ->join('INNER', $db->quoteName('#__extensions', 'e') . ' ON e.extension_id = use.extension_id')
+ ->where($db->quoteName('e.element') . ' = ' . $db->quote('mokoonyx'))
+ ->setLimit(1)
+ );
+ $siteId = (int) $db->loadResult();
+ if ($siteId > 0)
+ {
+ $db->setQuery(
+ $db->getQuery(true)
+ ->update($db->quoteName('#__update_sites'))
+ ->set($db->quoteName('extra_query') . ' = ' . $db->quote($this->savedDownloadKey))
+ ->where($db->quoteName('update_site_id') . ' = ' . $siteId)
+ )->execute();
+ }
+ }
+ catch (\Throwable $e) {}
+ }
+
+ private function warnMissingLicenseKey(): void
+ {
+ try
+ {
+ $db = \Joomla\CMS\Factory::getDbo();
+ $db->setQuery(
+ $db->getQuery(true)
+ ->select([$db->quoteName('update_site_id'), $db->quoteName('extra_query')])
+ ->from($db->quoteName('#__update_sites'))
+ ->where('(' . $db->quoteName('name') . ' LIKE ' . $db->quote('%MokoOnyx%') . ' OR ' . $db->quoteName('location') . ' LIKE ' . $db->quote('%MokoOnyx%') . ')')
+ ->setLimit(1)
+ );
+ $site = $db->loadObject();
+
+ if ($site)
+ {
+ $eq = (string) ($site->extra_query ?? '');
+ if (!empty($eq) && strpos($eq, 'dlid=') !== false) { parse_str($eq, $p); if (!empty($p['dlid'])) { return; } }
+ $editUrl = 'index.php?option=com_installer&task=updatesite.edit&update_site_id=' . (int) $site->update_site_id;
+ }
+ else
+ {
+ $editUrl = 'index.php?option=com_installer&view=updatesites';
+ }
+
+ \Joomla\CMS\Factory::getApplication()->enqueueMessage(
+ 'Moko Consulting License Key Required — '
+ . 'No download key is configured. Updates will not be available until a valid license key is entered. '
+ . 'Enter License Key',
+ 'warning'
+ );
+ }
+ catch (\Throwable $e) {}
+ }
}
diff --git a/src/templateDetails.xml b/src/templateDetails.xml
index a8bd706..d2b770d 100644
--- a/src/templateDetails.xml
+++ b/src/templateDetails.xml
@@ -35,7 +35,7 @@
mokoonyx
- 02.20.00
+ 02.19.07-dev
script.php
2026-05-16
Jonathan Miller || Moko Consulting
diff --git a/updates.xml b/updates.xml
deleted file mode 100644
index eb3c962..0000000
--- a/updates.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
-
- Template - MokoOnyx
- Template - MokoOnyx development build.
- mokoonyx
- template
- site
- 02.19.06-dev
- 2026-06-04
- https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/development
-
- https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/development/tpl_mokoonyx-02.19.06-dev.zip
-
- 718dd24295a5c8e3a0c855b01f86bf619bae84a4f7ab6e037c5cd5bfaadb2e30
- dev
- https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/raw/branch/main/CHANGELOG.md
- Moko Consulting
- https://mokoconsulting.tech
-
- 8.1.0
-
-
- Template - MokoOnyx
- Template - MokoOnyx stable build.
- mokoonyx
- template
- site
- 02.20.00
- 2026-06-04
- https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/stable
-
- https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/stable/tpl_mokoonyx-02.20.00.zip
-
- 00851a8a0ce9f64b873cbfbf4495422e78788d303ad5a4ac13f48122a8bc9e30
- stable
- https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/raw/branch/main/CHANGELOG.md
- Moko Consulting
- https://mokoconsulting.tech
-
- 8.1.0
-
-