36 Commits
stable ... main

Author SHA1 Message Date
f2494de8b9 chore: bump patch version for release pipeline fixes [skip ci] 2026-04-24 00:34:50 +00:00
8d9653e0c9 chore: add mokoconsulting-tech/enterprise dependency 2026-04-23 23:39:05 +00:00
a3288c0042 chore: sync updates.xml stable 01.00.25 [skip ci] 2026-04-23 23:34:25 +00:00
203324a40b chore: sync updates.xml development 01.00.24 [skip ci] 2026-04-23 23:30:51 +00:00
f32fda306a chore: sync auto-release.yml — remove skip gates blocking patches [skip ci] 2026-04-23 18:18:36 -05:00
c38f5752b6 chore: sync updates.xml development 01.00.23 [skip ci] 2026-04-23 23:11:23 +00:00
1b4984ed18 chore: sync updates.xml development 01.00.22 [skip ci] 2026-04-23 23:07:41 +00:00
ee8fc55d03 chore: sync updates.xml development 01.00.21 [skip ci] 2026-04-23 22:55:12 +00:00
f93d9c3e73 fix: remove GitHub mirror URLs from updates.xml (404)
GitHub mirror releases not syncing — remove broken URLs so
Joomla updater only uses the working Gitea download URL.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 22:43:07 +00:00
71cc1b6465 chore: sync updates.xml stable 01.00.20 [skip ci] 2026-04-23 20:25:57 +00:00
b51ebf5534 fix: remove theme preview tab and test page (#4)
fix: remove theme preview tab and test page
2026-04-23 20:18:42 +00:00
e9a0be9471 feat: lock extension on install/update
Sets locked=1 in #__extensions after install/update.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 20:14:57 +00:00
1cf063e08e Release 01.00.18 — minify pipeline, header color var, hero centering 2026-04-23 20:01:26 +00:00
e3349bc043 chore: sync update-server.yml with push triggers + main sync [skip ci] 2026-04-23 14:43:30 -05:00
fb9e9ddc48 fix: add hero card centering (flex + margin auto)
Some checks failed
Repo Health / Access control (push) Successful in 1s
Standards Compliance / Secret Scanning (push) Successful in 2s
Standards Compliance / License Header Validation (push) Successful in 3s
Standards Compliance / Repository Structure Validation (push) Successful in 2s
Standards Compliance / Coding Standards Check (push) Failing after 3s
Standards Compliance / Version Consistency Check (push) Successful in 3s
Standards Compliance / Workflow Configuration Check (push) Failing after 3s
Standards Compliance / Documentation Quality Check (push) Successful in 2s
Standards Compliance / README Completeness Check (push) Successful in 4s
Standards Compliance / Git Repository Hygiene (push) Successful in 3s
Standards Compliance / Line Length Check (push) Failing after 3s
Standards Compliance / Script Integrity Validation (push) Successful in 3s
Standards Compliance / File Naming Standards (push) Successful in 2s
Standards Compliance / Insecure Code Pattern Detection (push) Successful in 3s
Standards Compliance / Code Complexity Analysis (push) Successful in 4s
Standards Compliance / Code Duplication Detection (push) Successful in 3s
Standards Compliance / File Size Limits (push) Successful in 3s
Standards Compliance / Dead Code Detection (push) Successful in 3s
Standards Compliance / TODO/FIXME Tracking (push) Successful in 3s
Standards Compliance / Binary File Detection (push) Successful in 3s
Standards Compliance / Dependency Vulnerability Scanning (push) Successful in 4s
Standards Compliance / Unused Dependencies Check (push) Successful in 6s
Standards Compliance / Broken Link Detection (push) Successful in 4s
Standards Compliance / API Documentation Coverage (push) Successful in 3s
Standards Compliance / Accessibility Check (push) Successful in 3s
Standards Compliance / Performance Metrics (push) Successful in 2s
Standards Compliance / Enterprise Readiness Check (push) Successful in 3s
Standards Compliance / Repository Health Check (push) Successful in 4s
Repo Health / Release configuration (push) Failing after 2s
Standards Compliance / Terraform Configuration Validation (push) Successful in 5s
Repo Health / Scripts governance (push) Successful in 3s
Repo Health / Repository health (push) Failing after 3s
CodeQL Security Scanning / Analyze (javascript) (push) Failing after 1m25s
CodeQL Security Scanning / Analyze (actions) (push) Failing after 1m25s
Standards Compliance / Compliance Summary (push) Successful in 0s
CodeQL Security Scanning / Security Scan Summary (push) Successful in 1s
2026-04-23 19:09:10 +00:00
d6e0036a0b chore: sync updates.xml development 01.00.18 [skip ci] 2026-04-23 06:59:19 +00:00
6350b9cae7 chore: update stable channel to 01.00.17 on main [skip ci] 2026-04-22 08:15:14 +00:00
009bdf3bcf chore: update development channel to 01.00.16 on main [skip ci] 2026-04-22 07:36:51 +00:00
08aebca9b2 chore: update development channel to 01.00.15 on main [skip ci] 2026-04-22 07:15:31 +00:00
7b74e09e2b chore: update development channel to 01.00.14 on main [skip ci] 2026-04-22 07:11:38 +00:00
1771a719c8 chore: update development channel to 01.00.13 on main [skip ci] 2026-04-22 07:08:45 +00:00
adc1172829 chore: update development channel to 01.00.12 on main [skip ci] 2026-04-22 02:50:44 +00:00
be63ddd8ec chore: update stable channel to 01.00.11 on main [skip ci] 2026-04-22 02:08:06 +00:00
0055b7eaaf fix: update VERSION header in updates.xml to match stable 01.00.10
Some checks failed
CodeQL Security Scanning / Analyze (actions) (push) Failing after 1m14s
Repo Health / Access control (push) Successful in 1s
Standards Compliance / Secret Scanning (push) Successful in 3s
Standards Compliance / License Header Validation (push) Successful in 2s
CodeQL Security Scanning / Analyze (javascript) (push) Failing after 1m15s
Standards Compliance / Repository Structure Validation (push) Successful in 3s
Standards Compliance / Coding Standards Check (push) Failing after 4s
Standards Compliance / Version Consistency Check (push) Successful in 3s
Standards Compliance / Workflow Configuration Check (push) Failing after 3s
Standards Compliance / Documentation Quality Check (push) Successful in 3s
Standards Compliance / README Completeness Check (push) Successful in 2s
Standards Compliance / Git Repository Hygiene (push) Successful in 2s
Standards Compliance / Line Length Check (push) Failing after 2s
Standards Compliance / Script Integrity Validation (push) Successful in 4s
Standards Compliance / File Naming Standards (push) Successful in 3s
Standards Compliance / Insecure Code Pattern Detection (push) Successful in 2s
Standards Compliance / Code Complexity Analysis (push) Successful in 4s
Standards Compliance / Code Duplication Detection (push) Successful in 4s
Standards Compliance / File Size Limits (push) Successful in 2s
Standards Compliance / Dead Code Detection (push) Successful in 3s
Standards Compliance / TODO/FIXME Tracking (push) Successful in 3s
Standards Compliance / Binary File Detection (push) Successful in 4s
Standards Compliance / Dependency Vulnerability Scanning (push) Successful in 4s
Standards Compliance / Broken Link Detection (push) Successful in 4s
Standards Compliance / Unused Dependencies Check (push) Successful in 7s
Standards Compliance / API Documentation Coverage (push) Successful in 2s
Standards Compliance / Accessibility Check (push) Successful in 3s
Standards Compliance / Performance Metrics (push) Successful in 2s
Standards Compliance / Enterprise Readiness Check (push) Successful in 4s
Standards Compliance / Repository Health Check (push) Successful in 4s
Standards Compliance / Terraform Configuration Validation (push) Successful in 6s
Repo Health / Release configuration (push) Failing after 3s
Repo Health / Scripts governance (push) Successful in 3s
Repo Health / Repository health (push) Failing after 4s
CodeQL Security Scanning / Security Scan Summary (push) Successful in 1s
Standards Compliance / Compliance Summary (push) Successful in 1s
Auto-Assign Issues & PRs / Assign unassigned issues and PRs (push) Successful in 1s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 02:07:47 +00:00
faedeea34a chore: update development channel to 01.00.10 on main [skip ci] 2026-04-21 22:48:15 +00:00
e070cb99f5 chore: update development channel to 01.00.09 on main [skip ci] 2026-04-21 22:23:35 +00:00
41bc58a8d0 chore: update development channel to 01.00.08 on main [skip ci] 2026-04-21 21:19:58 +00:00
8d53b2c80f chore: update stable channel to 01.00.07 on main [skip ci] 2026-04-21 19:33:48 +00:00
f781e0d9ae chore: update development channel to 01.00.06 on main [skip ci] 2026-04-21 19:22:19 +00:00
ffa262f22b chore: update development channel to 01.00.05 on main [skip ci] 2026-04-21 19:02:01 +00:00
b16b31c1de chore: update development channel to 01.00.04 on main [skip ci] 2026-04-21 18:58:48 +00:00
12a34619f2 chore: update development channel to 01.00.02 on main [skip ci] 2026-04-21 18:49:23 +00:00
d69def86df chore: update development channel to 01.00.01 on main [skip ci] 2026-04-21 17:42:47 +00:00
b4d843981b fix: correct updates.xml URLs, versions, and SHA format [skip ci] 2026-04-21 17:01:16 +00:00
gitea-actions[bot]
3de10bd2a2 chore: Update SHA-256 hash for release v01 - SHA: 0019dfc4b32d63c1392aa264aed2253c1e0c2fb09216f8e2cc269bbfb8bb49b5 2026-04-19 22:30:50 +00:00
e9536a6998 chore: update stable channel to 01.00.00 on main [skip ci] 2026-04-19 22:30:49 +00:00
9 changed files with 541 additions and 846 deletions

View File

@@ -5,7 +5,7 @@
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: MokoStandards.Release # INGROUP: MokoStandards.Release
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards # REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/MokoStandards-API
# PATH: /templates/workflows/joomla/auto-release.yml.template # PATH: /templates/workflows/joomla/auto-release.yml.template
# VERSION: 04.06.00 # VERSION: 04.06.00
# BRIEF: Joomla build & release — ZIP package, updates.xml, SHA-256 checksum # BRIEF: Joomla build & release — ZIP package, updates.xml, SHA-256 checksum
@@ -22,13 +22,15 @@
# | 4. Update [VERSION: XX.YY.ZZ] badges in markdown files | # | 4. Update [VERSION: XX.YY.ZZ] badges in markdown files |
# | 5. Write updates.xml (Joomla update server XML) | # | 5. Write updates.xml (Joomla update server XML) |
# | 6. Create git tag vXX.YY.ZZ | # | 6. Create git tag vXX.YY.ZZ |
# | 7a. Patch: update existing GitHub Release for this minor | # | 7a. Patch: update existing Gitea Release for this minor |
# | 8. Build ZIP, upload asset, write SHA-256 to updates.xml | # | 8. Build ZIP, upload asset, write SHA-256 to updates.xml |
# | | # | |
# | Every version change: archives main -> version/XX.YY branch | # | Every version change: archives main -> version/XX.YY branch |
# | Patch 00 = development (no release). First release = patch 01. | # | All patches release (including 00). Patch 00/01 = full pipeline. |
# | First release only (patch == 01): | # | First release only (patch == 01): |
# | 7b. Create new GitHub Release | # | 7b. Create new Gitea Release |
# | |
# | GitHub mirror: stable/rc releases only (continue-on-error) |
# | | # | |
# +========================================================================+ # +========================================================================+
@@ -46,6 +48,9 @@ on:
env: env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true 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: permissions:
contents: write contents: write
@@ -61,19 +66,21 @@ jobs:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with: with:
token: ${{ secrets.GA_TOKEN || github.token }} token: ${{ secrets.GA_TOKEN }}
fetch-depth: 0 fetch-depth: 0
- name: Set authenticated push URL
run: git remote set-url origin "https://jmiller:${{ secrets.GA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
- name: Setup MokoStandards tools - name: Setup MokoStandards tools
env: env:
GA_TOKEN: ${{ secrets.GA_TOKEN || github.token }} MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN }}
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GA_TOKEN || github.token }}"}}' MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN }}"}}'
run: | run: |
git clone --depth 1 --branch version/04 --quiet \ # Ensure PHP + Composer are available
"https://x-access-token:${GH_TOKEN}@git.mokoconsulting.tech/MokoConsulting/MokoStandards-API.git" \ 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 /tmp/mokostandards-api
cd /tmp/mokostandards-api cd /tmp/mokostandards-api
composer install --no-dev --no-interaction --quiet composer install --no-dev --no-interaction --quiet
@@ -99,20 +106,15 @@ jobs:
echo "branch=version/${MAJOR}" >> "$GITHUB_OUTPUT" echo "branch=version/${MAJOR}" >> "$GITHUB_OUTPUT"
echo "minor=$MINOR" >> "$GITHUB_OUTPUT" echo "minor=$MINOR" >> "$GITHUB_OUTPUT"
echo "major=$MAJOR" >> "$GITHUB_OUTPUT" echo "major=$MAJOR" >> "$GITHUB_OUTPUT"
echo "release_tag=v${MAJOR}" >> "$GITHUB_OUTPUT" echo "release_tag=stable" >> "$GITHUB_OUTPUT"
if [ "$PATCH" = "00" ]; then echo "stability=stable" >> "$GITHUB_OUTPUT"
echo "skip=true" >> "$GITHUB_OUTPUT" echo "skip=false" >> "$GITHUB_OUTPUT"
echo "is_minor=false" >> "$GITHUB_OUTPUT" if [ "$PATCH" = "00" ] || [ "$PATCH" = "01" ]; then
echo "Version: $VERSION (patch 00 = development — skipping release)" echo "is_minor=true" >> "$GITHUB_OUTPUT"
echo "Version: $VERSION (first release for this minor — full pipeline)"
else else
echo "skip=false" >> "$GITHUB_OUTPUT" echo "is_minor=false" >> "$GITHUB_OUTPUT"
if [ "$PATCH" = "01" ]; then echo "Version: $VERSION (patch — platform version + badges only)"
echo "is_minor=true" >> "$GITHUB_OUTPUT"
echo "Version: $VERSION (first release — full pipeline)"
else
echo "is_minor=false" >> "$GITHUB_OUTPUT"
echo "Version: $VERSION (patch — platform version + badges only)"
fi
fi fi
- name: Check if already released - name: Check if already released
@@ -131,11 +133,8 @@ jobs:
echo "tag_exists=$TAG_EXISTS" >> "$GITHUB_OUTPUT" echo "tag_exists=$TAG_EXISTS" >> "$GITHUB_OUTPUT"
echo "branch_exists=$BRANCH_EXISTS" >> "$GITHUB_OUTPUT" echo "branch_exists=$BRANCH_EXISTS" >> "$GITHUB_OUTPUT"
if [ "$TAG_EXISTS" = "true" ] && [ "$BRANCH_EXISTS" = "true" ]; then # Tag and branch may persist across patch releases — never skip
echo "already_released=true" >> "$GITHUB_OUTPUT" echo "already_released=false" >> "$GITHUB_OUTPUT"
else
echo "already_released=false" >> "$GITHUB_OUTPUT"
fi
# -- SANITY CHECKS ------------------------------------------------------- # -- SANITY CHECKS -------------------------------------------------------
- name: "Sanity: Pre-release validation" - name: "Sanity: Pre-release validation"
@@ -291,9 +290,15 @@ jobs:
[ -z "$EXT_NAME" ] && EXT_NAME="${{ github.event.repository.name }}" [ -z "$EXT_NAME" ] && EXT_NAME="${{ github.event.repository.name }}"
[ -z "$EXT_TYPE" ] && EXT_TYPE="component" [ -z "$EXT_TYPE" ] && EXT_TYPE="component"
# Templates/modules don't have <element> — derive from <name> (lowercased) # Derive element if not in manifest:
# 1. Try XML filename (e.g. mokowaas.xml → mokowaas)
# 2. Fall back to repo name (lowercased)
if [ -z "$EXT_ELEMENT" ]; then if [ -z "$EXT_ELEMENT" ]; then
EXT_ELEMENT=$(echo "$EXT_NAME" | tr '[:upper:]' '[:lower:]' | tr -d ' ') EXT_ELEMENT=$(basename "$MANIFEST" .xml | tr '[:upper:]' '[:lower:]')
# If filename is generic (templateDetails, manifest), use repo name
case "$EXT_ELEMENT" in
templatedetails|manifest|*.xml) EXT_ELEMENT=$(echo "${{ github.event.repository.name }}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') ;;
esac
fi fi
# Build client tag: plugins and frontend modules need <client>site</client> # Build client tag: plugins and frontend modules need <client>site</client>
@@ -312,7 +317,7 @@ jobs:
# Build targetplatform (fallback to Joomla 5 if not in manifest) # Build targetplatform (fallback to Joomla 5 if not in manifest)
if [ -z "$TARGET_PLATFORM" ]; then if [ -z "$TARGET_PLATFORM" ]; then
TARGET_PLATFORM=$(printf '<targetplatform name="joomla" version="5.*" %s>' "/") TARGET_PLATFORM=$(printf '<targetplatform name="joomla" version="((5.[0-9])|(6.[0-9]))" %s>' "/")
fi fi
# Build php_minimum tag # Build php_minimum tag
@@ -321,11 +326,12 @@ jobs:
PHP_TAG="<php_minimum>${PHP_MINIMUM}</php_minimum>" PHP_TAG="<php_minimum>${PHP_MINIMUM}</php_minimum>"
fi fi
DOWNLOAD_URL="https://git.mokoconsulting.tech/${{ github.repository }}/releases/download/v${VERSION}/${EXT_ELEMENT}-${VERSION}.zip" DOWNLOAD_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/download/stable/${EXT_ELEMENT}-${VERSION}.zip"
INFO_URL="https://git.mokoconsulting.tech/${{ github.repository }}/releases/tag/v${VERSION}" INFO_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/stable"
# -- Build stable entry to temp file ───────────────────────── # -- Build update entry for a given stability tag
{ build_entry() {
local TAG_NAME="$1"
printf '%s\n' ' <update>' printf '%s\n' ' <update>'
printf '%s\n' " <name>${EXT_NAME}</name>" printf '%s\n' " <name>${EXT_NAME}</name>"
printf '%s\n' " <description>${EXT_NAME} update</description>" printf '%s\n' " <description>${EXT_NAME} update</description>"
@@ -334,9 +340,7 @@ jobs:
printf '%s\n' " <version>${VERSION}</version>" printf '%s\n' " <version>${VERSION}</version>"
[ -n "$CLIENT_TAG" ] && printf '%s\n' " ${CLIENT_TAG}" [ -n "$CLIENT_TAG" ] && printf '%s\n' " ${CLIENT_TAG}"
[ -n "$FOLDER_TAG" ] && printf '%s\n' " ${FOLDER_TAG}" [ -n "$FOLDER_TAG" ] && printf '%s\n' " ${FOLDER_TAG}"
printf '%s\n' ' <tags>' printf '%s\n' " <tags><tag>${TAG_NAME}</tag></tags>"
printf '%s\n' ' <tag>stable</tag>'
printf '%s\n' ' </tags>'
printf '%s\n' " <infourl title=\"${EXT_NAME}\">${INFO_URL}</infourl>" printf '%s\n' " <infourl title=\"${EXT_NAME}\">${INFO_URL}</infourl>"
printf '%s\n' ' <downloads>' printf '%s\n' ' <downloads>'
printf '%s\n' " <downloadurl type=\"full\" format=\"zip\">${DOWNLOAD_URL}</downloadurl>" printf '%s\n' " <downloadurl type=\"full\" format=\"zip\">${DOWNLOAD_URL}</downloadurl>"
@@ -346,35 +350,27 @@ jobs:
printf '%s\n' ' <maintainer>Moko Consulting</maintainer>' printf '%s\n' ' <maintainer>Moko Consulting</maintainer>'
printf '%s\n' ' <maintainerurl>https://mokoconsulting.tech</maintainerurl>' printf '%s\n' ' <maintainerurl>https://mokoconsulting.tech</maintainerurl>'
printf '%s\n' ' </update>' printf '%s\n' ' </update>'
} > /tmp/stable_entry.xml }
# -- Write updates.xml preserving dev/rc entries ──────────────
# Extract existing entries for other stability levels
# Order reflects release workflow: development → alpha → beta → rc → stable
if [ -f "updates.xml" ]; then
printf 'import re, sys\n' > /tmp/extract.py
printf 'with open("updates.xml") as f: c = f.read()\n' >> /tmp/extract.py
printf 'tag = sys.argv[1]\n' >> /tmp/extract.py
printf 'm = re.search(r"( <update>.*?<tag>" + re.escape(tag) + r"</tag>.*?</update>)", c, re.DOTALL)\n' >> /tmp/extract.py
printf 'if m: print(m.group(1))\n' >> /tmp/extract.py
fi
DEV_ENTRY=$(python3 /tmp/extract.py development 2>/dev/null || true)
ALPHA_ENTRY=$(python3 /tmp/extract.py alpha 2>/dev/null || true)
BETA_ENTRY=$(python3 /tmp/extract.py beta 2>/dev/null || true)
RC_ENTRY=$(python3 /tmp/extract.py rc 2>/dev/null || true)
# -- Write updates.xml with cascading channels
# Stable release updates ALL channels (development, alpha, beta, rc, stable)
{ {
printf '%s\n' '<?xml version="1.0" encoding="utf-8"?>' printf '%s\n' "<?xml version='1.0' encoding='UTF-8'?>"
printf '%s\n' "<!-- Copyright (C) $(date +%Y) Moko Consulting <hello@mokoconsulting.tech>"
printf '%s\n' " SPDX-License-Identifier: GPL-3.0-or-later"
printf '%s\n' " VERSION: ${VERSION}"
printf '%s\n' " -->"
printf '%s\n' ""
printf '%s\n' '<updates>' printf '%s\n' '<updates>'
[ -n "$DEV_ENTRY" ] && echo "$DEV_ENTRY" build_entry "development"
[ -n "$ALPHA_ENTRY" ] && echo "$ALPHA_ENTRY" build_entry "alpha"
[ -n "$BETA_ENTRY" ] && echo "$BETA_ENTRY" build_entry "beta"
[ -n "$RC_ENTRY" ] && echo "$RC_ENTRY" build_entry "rc"
cat /tmp/stable_entry.xml build_entry "stable"
printf '%s\n' '</updates>' printf '%s\n' '</updates>'
} > updates.xml } > updates.xml
echo "updates.xml: ${VERSION} (stable + rc/dev preserved)" >> $GITHUB_STEP_SUMMARY echo "updates.xml: ${VERSION} (all channels updated to stable)" >> $GITHUB_STEP_SUMMARY
# -- Commit all changes --------------------------------------------------- # -- Commit all changes ---------------------------------------------------
- name: Commit release changes - name: Commit release changes
@@ -389,10 +385,12 @@ jobs:
VERSION="${{ steps.version.outputs.version }}" VERSION="${{ steps.version.outputs.version }}"
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
git config --local user.name "gitea-actions[bot]" git config --local user.name "gitea-actions[bot]"
# Set push URL with token for branch-protected repos
git remote set-url origin "https://jmiller:${{ secrets.GA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
git add -A git add -A
git commit -m "chore(release): build ${VERSION} [skip ci]" \ git commit -m "chore(release): build ${VERSION} [skip ci]" \
--author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>" --author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>"
git push git push -u origin HEAD
# -- STEP 6: Create tag --------------------------------------------------- # -- STEP 6: Create tag ---------------------------------------------------
- name: "Step 6: Create git tag" - name: "Step 6: Create git tag"
@@ -412,69 +410,75 @@ jobs:
fi fi
echo "Tag: ${TAG}" >> $GITHUB_STEP_SUMMARY echo "Tag: ${TAG}" >> $GITHUB_STEP_SUMMARY
# -- STEP 7: Create or update GitHub Release ------------------------------ # -- STEP 7: Create or update Gitea Release --------------------------------
- name: "Step 7: GitHub Release" - name: "Step 7: Gitea Release"
if: >- if: >-
steps.version.outputs.skip != 'true' && steps.version.outputs.skip != 'true'
steps.check.outputs.tag_exists != 'true'
env:
GH_TOKEN: ${{ secrets.GA_TOKEN || github.token }}
run: | run: |
VERSION="${{ steps.version.outputs.version }}" VERSION="${{ steps.version.outputs.version }}"
RELEASE_TAG="${{ steps.version.outputs.release_tag }}" RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
BRANCH="${{ steps.version.outputs.branch }}" BRANCH="${{ steps.version.outputs.branch }}"
MAJOR="${{ steps.version.outputs.major }}" MAJOR="${{ steps.version.outputs.major }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
NOTES=$(php /tmp/mokostandards-api/cli/release_notes.php --path . --version "$VERSION" 2>/dev/null) NOTES=$(php /tmp/mokostandards-api/cli/release_notes.php --path . --version "$VERSION" 2>/dev/null)
[ -z "$NOTES" ] && NOTES="Release ${VERSION}" [ -z "$NOTES" ] && NOTES="Release ${VERSION}"
echo "$NOTES" > /tmp/release_notes.md
# Check if the major release already exists # Check if the major release already exists
EXISTING=$(gh release view "$RELEASE_TAG" --json tagName -q .tagName 2>/dev/null || true) EXISTING=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
"${API_BASE}/releases/tags/${RELEASE_TAG}" 2>/dev/null || true)
EXISTING_ID=$(echo "$EXISTING" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('id',''))" 2>/dev/null || true)
if [ -z "$EXISTING" ]; then if [ -z "$EXISTING_ID" ]; then
# First release for this major # First release for this major
gh release create "$RELEASE_TAG" \ curl -sf -X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
--title "v${MAJOR} (latest: ${VERSION})" \ -H "Content-Type: application/json" \
--notes-file /tmp/release_notes.md \ "${API_BASE}/releases" \
--target "$BRANCH" -d "$(python3 -c "import json; print(json.dumps({
'tag_name': '${RELEASE_TAG}',
'name': 'v${MAJOR} (latest: ${VERSION})',
'body': '''${NOTES}''',
'target_commitish': '${BRANCH}'
}))")"
echo "Release created: ${RELEASE_TAG} (${VERSION})" >> $GITHUB_STEP_SUMMARY echo "Release created: ${RELEASE_TAG} (${VERSION})" >> $GITHUB_STEP_SUMMARY
else else
# Append version notes to existing major release # Append version notes to existing major release
CURRENT_NOTES=$(gh release view "$RELEASE_TAG" --json body -q .body 2>/dev/null || true) CURRENT_BODY=$(echo "$EXISTING" | python3 -c "import sys,json; print(json.load(sys.stdin).get('body',''))" 2>/dev/null || true)
{ UPDATED_BODY="${CURRENT_BODY}
echo "$CURRENT_NOTES"
echo ""
echo "---"
echo "### ${VERSION}"
echo ""
cat /tmp/release_notes.md
} > /tmp/updated_notes.md
gh release edit "$RELEASE_TAG" \ ---
--title "v${MAJOR} (latest: ${VERSION})" \ ### ${VERSION}
--notes-file /tmp/updated_notes.md
${NOTES}"
curl -sf -X PATCH -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
-H "Content-Type: application/json" \
"${API_BASE}/releases/${EXISTING_ID}" \
-d "$(python3 -c "import json,sys; print(json.dumps({
'name': 'v${MAJOR} (latest: ${VERSION})',
'body': sys.stdin.read()
}))" <<< "$UPDATED_BODY")"
echo "Release updated: ${RELEASE_TAG} -> ${VERSION}" >> $GITHUB_STEP_SUMMARY echo "Release updated: ${RELEASE_TAG} -> ${VERSION}" >> $GITHUB_STEP_SUMMARY
fi fi
# -- STEP 8: Build Joomla install ZIP + SHA-256 checksum ------------------ # -- STEP 8: Build Joomla install ZIP + SHA-256 checksum ------------------
# Every patch builds an install-ready ZIP and uploads it to the minor release.
# Result: one Release per minor version with a ZIP for each patch.
- name: "Step 8: Build Joomla package and update checksum" - name: "Step 8: Build Joomla package and update checksum"
if: >- if: >-
steps.version.outputs.skip != 'true' steps.version.outputs.skip != 'true'
env:
GH_TOKEN: ${{ secrets.GA_TOKEN || github.token }}
run: | run: |
VERSION="${{ steps.version.outputs.version }}" VERSION="${{ steps.version.outputs.version }}"
RELEASE_TAG="${{ steps.version.outputs.release_tag }}" RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
REPO="${{ github.repository }}" REPO="${{ github.repository }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
# All ZIPs upload to the major release tag (vXX) # All ZIPs upload to the major release tag (vXX)
gh release view "$RELEASE_TAG" --json tagName > /dev/null 2>&1 || { 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
echo "No release ${RELEASE_TAG} found — skipping ZIP upload" echo "No release ${RELEASE_TAG} found — skipping ZIP upload"
exit 0 exit 0
} fi
# Find extension element name from manifest # Find extension element name from manifest
MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1 || true) MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1 || true)
@@ -508,27 +512,109 @@ jobs:
SHA256_ZIP=$(sha256sum "/tmp/${ZIP_NAME}" | cut -d' ' -f1) SHA256_ZIP=$(sha256sum "/tmp/${ZIP_NAME}" | cut -d' ' -f1)
SHA256_TAR=$(sha256sum "/tmp/${TAR_NAME}" | cut -d' ' -f1) SHA256_TAR=$(sha256sum "/tmp/${TAR_NAME}" | cut -d' ' -f1)
# -- 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_NAME in "$ZIP_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_NAME}':
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 to release tag ---------------------------------- # -- Upload both to release tag ----------------------------------
gh release upload "$RELEASE_TAG" "/tmp/${ZIP_NAME}" --clobber 2>/dev/null || true curl -sf -X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
gh release upload "$RELEASE_TAG" "/tmp/${TAR_NAME}" --clobber 2>/dev/null || true -H "Content-Type: application/octet-stream" \
--data-binary @"/tmp/${ZIP_NAME}" \
"${API_BASE}/releases/${RELEASE_ID}/assets?name=${ZIP_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
# -- Update updates.xml with both download formats --------------- # -- Update updates.xml with both download formats ---------------
if [ -f "updates.xml" ]; then if [ -f "updates.xml" ]; then
ZIP_URL="https://git.mokoconsulting.tech/${{ github.repository }}/releases/download/${RELEASE_TAG}/${ZIP_NAME}" ZIP_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/download/${RELEASE_TAG}/${ZIP_NAME}"
TAR_URL="https://git.mokoconsulting.tech/${{ github.repository }}/releases/download/${RELEASE_TAG}/${TAR_NAME}" TAR_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/download/${RELEASE_TAG}/${TAR_NAME}"
# Replace downloads block with both formats + SHA # Use Python to update only the stable entry's downloads + sha256
sed -i "s|<downloads>.*</downloads>|<downloads>\n <downloadurl type=\"full\" format=\"zip\">${ZIP_URL}</downloadurl>\n <downloadurl type=\"full\" format=\"tar.gz\">${TAR_URL}</downloadurl>\n </downloads>|" updates.xml 2>/dev/null || true export PY_ZIP_URL="$ZIP_URL" PY_TAR_URL="$TAR_URL" PY_SHA="$SHA256_ZIP"
if grep -q '<sha256>' updates.xml; then python3 << 'PYEOF'
sed -i "s|<sha256>.*</sha256>|<sha256>${SHA256_ZIP}</sha256>|" updates.xml import re, os
else
sed -i "s|</downloads>|</downloads>\n <sha256>${SHA256_ZIP}</sha256>|" updates.xml
fi
with open("updates.xml") as f:
content = f.read()
zip_url = os.environ["PY_ZIP_URL"]
tar_url = os.environ["PY_TAR_URL"]
sha = os.environ["PY_SHA"]
# Find the stable update block and replace its downloads + sha256
def replace_stable(m):
block = m.group(0)
# Replace downloads block
new_downloads = (
" <downloads>\n"
f" <downloadurl type=\"full\" format=\"zip\">{zip_url}</downloadurl>\n"
" </downloads>"
)
block = re.sub(r' <downloads>.*?</downloads>', new_downloads, block, flags=re.DOTALL)
# Add or replace sha256
if '<sha256>' in block:
block = re.sub(r' <sha256>.*?</sha256>', f' <sha256>{sha}</sha256>', block)
else:
block = block.replace('</downloads>', f'</downloads>\n <sha256>{sha}</sha256>')
return block
content = re.sub(
r' <update>.*?<tag>stable</tag>.*?</update>',
replace_stable,
content,
flags=re.DOTALL
)
with open("updates.xml", "w") as f:
f.write(content)
PYEOF
CURRENT_BRANCH="${{ github.ref_name }}"
git add updates.xml git add updates.xml
git commit -m "chore(release): ZIP + tar.gz for ${VERSION} [skip ci]" \ git commit -m "chore(release): ZIP + tar.gz for ${VERSION} [skip ci]" \
--author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>" || true --author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>" || true
git push || true git push || true
# Sync updates.xml to main via direct API (always runs — may be on version/XX branch)
GA_TOKEN="${{ secrets.GA_TOKEN }}"
API="${GITEA_URL:-https://git.mokoconsulting.tech}/api/v1/repos/${{ github.repository }}"
FILE_SHA=$(curl -sf -H "Authorization: token ${GA_TOKEN}" \
"${API}/contents/updates.xml?ref=main" | jq -r '.sha // empty')
if [ -n "$FILE_SHA" ]; then
CONTENT=$(base64 -w0 updates.xml)
curl -sf -X PUT -H "Authorization: token ${GA_TOKEN}" \
-H "Content-Type: application/json" \
"${API}/contents/updates.xml" \
-d "$(jq -n \
--arg content "$CONTENT" \
--arg sha "$FILE_SHA" \
--arg msg "chore: sync updates.xml ${VERSION} [skip ci]" \
--arg branch "main" \
'{content: $content, sha: $sha, message: $msg, branch: $branch}'
)" > /dev/null 2>&1 \
&& echo "updates.xml synced to main via API" \
|| echo "WARNING: failed to sync updates.xml to main"
else
echo "WARNING: could not get updates.xml SHA from main"
fi
fi fi
echo "### Joomla Packages" >> $GITHUB_STEP_SUMMARY echo "### Joomla Packages" >> $GITHUB_STEP_SUMMARY
@@ -538,7 +624,50 @@ jobs:
echo "| \`${ZIP_NAME}\` | ${ZIP_SIZE} | \`${SHA256_ZIP}\` |" >> $GITHUB_STEP_SUMMARY echo "| \`${ZIP_NAME}\` | ${ZIP_SIZE} | \`${SHA256_ZIP}\` |" >> $GITHUB_STEP_SUMMARY
echo "| \`${TAR_NAME}\` | ${TAR_SIZE} | \`${SHA256_TAR}\` |" >> $GITHUB_STEP_SUMMARY echo "| \`${TAR_NAME}\` | ${TAR_SIZE} | \`${SHA256_TAR}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Release | \`${RELEASE_TAG}\` | |" >> $GITHUB_STEP_SUMMARY echo "| Release | \`${RELEASE_TAG}\` | |" >> $GITHUB_STEP_SUMMARY
echo "| Download | [${PACKAGE_NAME}](https://git.mokoconsulting.tech/${{ github.repository }}/releases/download/${RELEASE_TAG}/${PACKAGE_NAME}) |" >> $GITHUB_STEP_SUMMARY echo "| Download | [${ZIP_NAME}](${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/download/${RELEASE_TAG}/${ZIP_NAME}) |" >> $GITHUB_STEP_SUMMARY
# -- STEP 9: Mirror to GitHub (stable only) --------------------------------
- name: "Step 9: Mirror release to GitHub"
if: >-
steps.version.outputs.skip != 'true' &&
steps.version.outputs.stability == 'stable' &&
secrets.GH_TOKEN != ''
continue-on-error: true
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
run: |
VERSION="${{ steps.version.outputs.version }}"
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
MAJOR="${{ steps.version.outputs.major }}"
BRANCH="${{ steps.version.outputs.branch }}"
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
NOTES=$(php /tmp/mokostandards-api/cli/release_notes.php --path . --version "$VERSION" 2>/dev/null || true)
[ -z "$NOTES" ] && NOTES="Release ${VERSION}"
echo "$NOTES" > /tmp/release_notes.md
EXISTING=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" "${GITEA_URL:-https://git.mokoconsulting.tech}/api/v1/repos/${{ github.repository }}/releases/tags/$RELEASE_TAG" 2>/dev/null | jq -r ".tag_name // empty" || true)
if [ -z "$EXISTING" ]; then
gh release create "$RELEASE_TAG" \
--repo "$GH_REPO" \
--title "v${MAJOR} (latest: ${VERSION})" \
--notes-file /tmp/release_notes.md \
--target "$BRANCH" || true
else
gh release edit "$RELEASE_TAG" \
--repo "$GH_REPO" \
--title "v${MAJOR} (latest: ${VERSION})" || true
fi
# Upload assets to GitHub mirror
for PKG in /tmp/${EXT_ELEMENT:-pkg}-${VERSION}.*; do
if [ -f "$PKG" ]; then
_RELID=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" "${GITEA_URL:-https://git.mokoconsulting.tech}/api/v1/repos/${{ github.repository }}/releases/tags/$RELEASE_TAG" 2>/dev/null | jq -r ".id // empty")
[ -n "$_RELID" ] && curl -sf -X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" -H "Content-Type: application/octet-stream" "${GITEA_URL:-https://git.mokoconsulting.tech}/api/v1/repos/${{ github.repository }}/releases/${_RELID}/assets?name=$(basename $PKG)" --data-binary "@$PKG" > /dev/null 2>&1 || true
fi
done
echo "GitHub mirror updated: ${GH_REPO} ${RELEASE_TAG}" >> $GITHUB_STEP_SUMMARY
# -- Summary -------------------------------------------------------------- # -- Summary --------------------------------------------------------------
- name: Pipeline Summary - name: Pipeline Summary
@@ -559,5 +688,5 @@ jobs:
echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Branch | \`${{ steps.version.outputs.branch }}\` |" >> $GITHUB_STEP_SUMMARY echo "| Branch | \`${{ steps.version.outputs.branch }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Tag | \`${{ steps.version.outputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY echo "| Tag | \`${{ steps.version.outputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Release | [View](https://github.com/${{ github.repository }}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY echo "| Release | [View](${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY
fi fi

View File

@@ -5,7 +5,7 @@
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: MokoStandards.Joomla # INGROUP: MokoStandards.Joomla
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards # REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/MokoStandards-API
# PATH: /templates/workflows/joomla/update-server.yml.template # PATH: /templates/workflows/joomla/update-server.yml.template
# VERSION: 04.06.00 # VERSION: 04.06.00
# BRIEF: Update Joomla update server XML feed with stable/rc/dev entries # BRIEF: Update Joomla update server XML feed with stable/rc/dev entries
@@ -13,16 +13,27 @@
# Writes updates.xml with multiple <update> entries: # Writes updates.xml with multiple <update> entries:
# - <tag>stable</tag> on push to main (from auto-release) # - <tag>stable</tag> on push to main (from auto-release)
# - <tag>rc</tag> on push to rc/** # - <tag>rc</tag> on push to rc/**
# - <tag>development</tag> on push to dev/** # - <tag>development</tag> on push to dev or dev/**
# #
# Joomla filters by user's "Minimum Stability" setting. # Joomla filters by user's "Minimum Stability" setting.
name: Update Joomla Update Server XML Feed name: Update Joomla Update Server XML Feed
on: on:
push:
branches:
- 'dev'
- 'dev/**'
- 'alpha/**'
- 'beta/**'
- 'rc/**'
paths:
- 'src/**'
- 'htdocs/**'
pull_request: pull_request:
types: [closed] types: [closed]
branches: branches:
- 'dev'
- 'dev/**' - 'dev/**'
- 'alpha/**' - 'alpha/**'
- 'beta/**' - 'beta/**'
@@ -46,6 +57,9 @@ on:
env: env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true 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: permissions:
contents: write contents: write
@@ -55,45 +69,49 @@ jobs:
name: Update updates.xml name: Update updates.xml
runs-on: release runs-on: release
if: >- if: >-
github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' || github.event_name == 'push'
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with: with:
token: ${{ secrets.GA_TOKEN || github.token }} token: ${{ secrets.GA_TOKEN }}
fetch-depth: 0 fetch-depth: 0
- name: Setup MokoStandards tools - name: Setup MokoStandards tools
env: env:
GH_TOKEN: ${{ secrets.GA_TOKEN || github.token }} MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN }}
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GA_TOKEN || github.token }}"}}' MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN }}"}}'
run: | run: |
git clone --depth 1 --branch version/04 --quiet \ if ! command -v composer &> /dev/null; then
"https://x-access-token:${GH_TOKEN}@git.mokoconsulting.tech/MokoConsulting/MokoStandards.git" \ 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
/tmp/mokostandards 2>/dev/null || true fi
if [ -d "/tmp/mokostandards" ] && [ -f "/tmp/mokostandards/composer.json" ]; then git clone --depth 1 --branch main --quiet \
cd /tmp/mokostandards && composer install --no-dev --no-interaction --quiet 2>/dev/null || true "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 fi
- name: Generate updates.xml entry - name: Generate updates.xml entry
id: update
run: | run: |
BRANCH="${{ github.ref_name }}" BRANCH="${{ github.ref_name }}"
REPO="${{ github.repository }}" REPO="${{ github.repository }}"
VERSION=$(php /tmp/mokostandards/api/cli/version_read.php --path . 2>/dev/null || echo "0.0.0") 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 alpha/beta/rc branches (not dev — dev bumps manually) # Auto-bump patch on all branches (dev, alpha, beta, rc)
if [[ "$BRANCH" != dev/* ]]; then git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" git config --local user.name "gitea-actions[bot]"
git config --local user.name "gitea-actions[bot]" BUMPED=$(php /tmp/mokostandards-api/cli/version_bump.php --path . 2>/dev/null || true)
BUMPED=$(php /tmp/mokostandards/api/cli/version_bump.php --path . 2>/dev/null || true) if [ -n "$BUMPED" ]; then
if [ -n "$BUMPED" ]; then VERSION=$(php /tmp/mokostandards-api/cli/version_read.php --path . 2>/dev/null || echo "$VERSION")
VERSION=$(php /tmp/mokostandards/api/cli/version_read.php --path . 2>/dev/null || echo "$VERSION") git add -A
git add -A git commit -m "chore(version): auto-bump patch ${VERSION} [skip ci]" \
git commit -m "chore(version): auto-bump patch ${VERSION} [skip ci]" \ --author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>" 2>/dev/null || true
--author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>" 2>/dev/null || true git push 2>/dev/null || true
git push 2>/dev/null || true
fi
fi fi
# Determine stability from branch or input # Determine stability from branch or input
@@ -105,12 +123,14 @@ jobs:
STABILITY="beta" STABILITY="beta"
elif [[ "$BRANCH" == alpha/* ]]; then elif [[ "$BRANCH" == alpha/* ]]; then
STABILITY="alpha" STABILITY="alpha"
elif [[ "$BRANCH" == dev/* ]]; then elif [[ "$BRANCH" == dev/* ]] || [[ "$BRANCH" == "dev" ]]; then
STABILITY="development" STABILITY="development"
else else
STABILITY="stable" STABILITY="stable"
fi fi
echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT"
# Parse manifest (portable — no grep -P) # Parse manifest (portable — no grep -P)
MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1) MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
if [ -z "$MANIFEST" ]; then if [ -z "$MANIFEST" ]; then
@@ -132,15 +152,18 @@ jobs:
[ -z "$EXT_NAME" ] && EXT_NAME="${{ github.event.repository.name }}" [ -z "$EXT_NAME" ] && EXT_NAME="${{ github.event.repository.name }}"
[ -z "$EXT_TYPE" ] && EXT_TYPE="component" [ -z "$EXT_TYPE" ] && EXT_TYPE="component"
# Templates and modules don't have <element> — derive from <name> # Derive element if not in manifest: try XML filename, then repo name
if [ -z "$EXT_ELEMENT" ]; then if [ -z "$EXT_ELEMENT" ]; then
EXT_ELEMENT=$(echo "$EXT_NAME" | tr '[:upper:]' '[:lower:]' | tr -d ' ') 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 fi
# Use manifest version if README version is empty # Use manifest version if README version is empty
[ "$VERSION" = "0.0.0" ] && [ -n "$EXT_VERSION" ] && VERSION="$EXT_VERSION" [ "$VERSION" = "0.0.0" ] && [ -n "$EXT_VERSION" ] && VERSION="$EXT_VERSION"
[ -z "$TARGET_PLATFORM" ] && TARGET_PLATFORM=$(printf '<targetplatform name="joomla" version="5.*" %s>' "/") [ -z "$TARGET_PLATFORM" ] && TARGET_PLATFORM=$(printf '<targetplatform name="joomla" version="((5.[0-9])|(6.[0-9]))" %s>' "/")
CLIENT_TAG="" CLIENT_TAG=""
[ -n "$EXT_CLIENT" ] && CLIENT_TAG="<client>${EXT_CLIENT}</client>" [ -n "$EXT_CLIENT" ] && CLIENT_TAG="<client>${EXT_CLIENT}</client>"
@@ -173,10 +196,10 @@ jobs:
esac esac
PACKAGE_NAME="${EXT_ELEMENT}-${DISPLAY_VERSION}.zip" PACKAGE_NAME="${EXT_ELEMENT}-${DISPLAY_VERSION}.zip"
DOWNLOAD_URL="https://git.mokoconsulting.tech/${{ github.repository }}/releases/download/${RELEASE_TAG}/${PACKAGE_NAME}" DOWNLOAD_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/download/${RELEASE_TAG}/${PACKAGE_NAME}"
INFO_URL="https://github.com/${REPO}" INFO_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}"
# ── Build install packages (ZIP + tar.gz) ─────────────────── # -- Build install packages (ZIP + tar.gz) --------------------
SOURCE_DIR="src" SOURCE_DIR="src"
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs" [ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
if [ -d "$SOURCE_DIR" ]; then if [ -d "$SOURCE_DIR" ]; then
@@ -192,20 +215,62 @@ jobs:
SHA256=$(sha256sum "/tmp/${PACKAGE_NAME}" | cut -d' ' -f1) SHA256=$(sha256sum "/tmp/${PACKAGE_NAME}" | cut -d' ' -f1)
# Ensure release exists # Ensure release exists on Gitea
gh release view "$RELEASE_TAG" --json tagName > /dev/null 2>&1 || \ RELEASE_JSON=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
gh release create "$RELEASE_TAG" --title "${RELEASE_TAG} (${DISPLAY_VERSION})" --notes "${STABILITY} release" --prerelease --target main 2>/dev/null || true "${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)
# Upload both formats if [ -z "$RELEASE_ID" ]; then
gh release upload "$RELEASE_TAG" "/tmp/${PACKAGE_NAME}" --clobber 2>/dev/null || true # Create release
gh release upload "$RELEASE_TAG" "/tmp/${TAR_NAME}" --clobber 2>/dev/null || true 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 echo "Packages: ${PACKAGE_NAME} + ${TAR_NAME} (SHA: ${SHA256})" >> $GITHUB_STEP_SUMMARY
else else
SHA256="" SHA256=""
fi fi
# ── Build the new entry ─────────────────────────────────────── # -- Build the new entry -----------------------------------------
NEW_ENTRY="" NEW_ENTRY=""
NEW_ENTRY="${NEW_ENTRY} <update>\n" NEW_ENTRY="${NEW_ENTRY} <update>\n"
NEW_ENTRY="${NEW_ENTRY} <name>${EXT_NAME}</name>\n" NEW_ENTRY="${NEW_ENTRY} <name>${EXT_NAME}</name>\n"
@@ -220,40 +285,76 @@ jobs:
NEW_ENTRY="${NEW_ENTRY} </tags>\n" NEW_ENTRY="${NEW_ENTRY} </tags>\n"
NEW_ENTRY="${NEW_ENTRY} <infourl title=\"${EXT_NAME}\">${INFO_URL}</infourl>\n" NEW_ENTRY="${NEW_ENTRY} <infourl title=\"${EXT_NAME}\">${INFO_URL}</infourl>\n"
NEW_ENTRY="${NEW_ENTRY} <downloads>\n" NEW_ENTRY="${NEW_ENTRY} <downloads>\n"
TAR_URL="https://git.mokoconsulting.tech/${{ github.repository }}/releases/download/${RELEASE_TAG}/${EXT_ELEMENT}-${DISPLAY_VERSION}.tar.gz"
NEW_ENTRY="${NEW_ENTRY} <downloadurl type=\"full\" format=\"zip\">${DOWNLOAD_URL}</downloadurl>\n" NEW_ENTRY="${NEW_ENTRY} <downloadurl type=\"full\" format=\"zip\">${DOWNLOAD_URL}</downloadurl>\n"
NEW_ENTRY="${NEW_ENTRY} <downloadurl type=\"full\" format=\"tar.gz\">${TAR_URL}</downloadurl>\n"
NEW_ENTRY="${NEW_ENTRY} </downloads>\n" NEW_ENTRY="${NEW_ENTRY} </downloads>\n"
[ -n "$SHA256" ] && NEW_ENTRY="${NEW_ENTRY} <sha256>sha256:${SHA256}</sha256>\n" [ -n "$SHA256" ] && NEW_ENTRY="${NEW_ENTRY} <sha256>${SHA256}</sha256>\n"
NEW_ENTRY="${NEW_ENTRY} ${TARGET_PLATFORM}\n" NEW_ENTRY="${NEW_ENTRY} ${TARGET_PLATFORM}\n"
[ -n "$PHP_TAG" ] && NEW_ENTRY="${NEW_ENTRY} ${PHP_TAG}\n" [ -n "$PHP_TAG" ] && NEW_ENTRY="${NEW_ENTRY} ${PHP_TAG}\n"
NEW_ENTRY="${NEW_ENTRY} <maintainer>Moko Consulting</maintainer>\n" NEW_ENTRY="${NEW_ENTRY} <maintainer>Moko Consulting</maintainer>\n"
NEW_ENTRY="${NEW_ENTRY} <maintainerurl>https://mokoconsulting.tech</maintainerurl>\n" NEW_ENTRY="${NEW_ENTRY} <maintainerurl>https://mokoconsulting.tech</maintainerurl>\n"
NEW_ENTRY="${NEW_ENTRY} </update>" NEW_ENTRY="${NEW_ENTRY} </update>"
# ── Write new entry to temp file ─────────────────────────────── # -- Write new entry to temp file --------------------------------
printf '%b' "$NEW_ENTRY" > /tmp/new_entry.xml printf '%b' "$NEW_ENTRY" > /tmp/new_entry.xml
# ── Merge into updates.xml ───────────────────────────────────── # -- Merge into updates.xml (only update this stability channel) -
# Cascading update: each stability level updates itself and all lower levels
# stable → all | rc → rc,beta,alpha,dev | beta → beta,alpha,dev | 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}"
if [ ! -f "updates.xml" ]; then if [ ! -f "updates.xml" ]; then
printf '%s\n' '<?xml version="1.0" encoding="utf-8"?>' > updates.xml printf '%s\n' "<?xml version='1.0' encoding='UTF-8'?>" > updates.xml
printf '%s\n' "<!-- Copyright (C) $(date +%Y) Moko Consulting <hello@mokoconsulting.tech>" >> updates.xml
printf '%s\n' " SPDX-License-Identifier: GPL-3.0-or-later" >> updates.xml
printf '%s\n' " VERSION: ${VERSION}" >> updates.xml
printf '%s\n' " -->" >> updates.xml
printf '%s\n' "" >> updates.xml
printf '%s\n' '<updates>' >> updates.xml printf '%s\n' '<updates>' >> updates.xml
cat /tmp/new_entry.xml >> updates.xml cat /tmp/new_entry.xml >> updates.xml
printf '\n%s\n' '</updates>' >> updates.xml printf '\n%s\n' '</updates>' >> updates.xml
else else
# Remove existing entry for this stability, insert new one # Replace each cascading channel with the new entry (different tag)
printf 'import re\nstability = "%s"\n' "${STABILITY}" > /tmp/merge_xml.py export PY_TARGETS="$TARGETS"
printf 'with open("updates.xml") as f: content = f.read()\n' >> /tmp/merge_xml.py python3 << PYEOF
printf 'with open("/tmp/new_entry.xml") as f: new_entry = f.read()\n' >> /tmp/merge_xml.py import re, os
printf 'pattern = r" <update>.*?<tag>" + re.escape(stability) + r"</tag>.*?</update>\\n?"\n' >> /tmp/merge_xml.py targets = os.environ["PY_TARGETS"].split(",")
printf 'content = re.sub(pattern, "", content, flags=re.DOTALL)\n' >> /tmp/merge_xml.py stability = "${STABILITY}"
printf 'content = content.replace("</updates>", new_entry + "\\n</updates>")\n' >> /tmp/merge_xml.py with open("updates.xml") as f:
printf 'content = re.sub(r"\\n{3,}", "\\n\\n", content)\n' >> /tmp/merge_xml.py content = f.read()
printf 'with open("updates.xml", "w") as f: f.write(content)\n' >> /tmp/merge_xml.py with open("/tmp/new_entry.xml") as f:
python3 /tmp/merge_xml.py 2>/dev/null || { new_entry_template = f.read()
for tag in targets:
tag = tag.strip()
# Build entry with this tag
new_entry = re.sub(r"<tag>[^<]*</tag>", f"<tag>{tag}</tag>", new_entry_template)
# Remove existing entry for this tag
pattern = r" <update>.*?<tag>" + re.escape(tag) + r"</tag>.*?</update>\n?"
content = re.sub(pattern, "", content, flags=re.DOTALL)
# Insert before </updates>
content = content.replace("</updates>", new_entry + "\n</updates>")
content = re.sub(r"\n{3,}", "\n\n", content)
with open("updates.xml", "w") as f:
f.write(content)
PYEOF
if [ $? -ne 0 ]; then
# Fallback: rebuild keeping other stability entries # Fallback: rebuild keeping other stability entries
{ {
printf '%s\n' '<?xml version="1.0" encoding="utf-8"?>' printf '%s\n' "<?xml version='1.0' encoding='UTF-8'?>"
printf '%s\n' "<!-- Copyright (C) $(date +%Y) Moko Consulting <hello@mokoconsulting.tech>"
printf '%s\n' " SPDX-License-Identifier: GPL-3.0-or-later"
printf '%s\n' " VERSION: ${VERSION}"
printf '%s\n' " -->"
printf '%s\n' ""
printf '%s\n' '<updates>' printf '%s\n' '<updates>'
for TAG in stable rc development; do for TAG in stable rc development; do
[ "$TAG" = "${STABILITY}" ] && continue [ "$TAG" = "${STABILITY}" ] && continue
@@ -265,7 +366,7 @@ jobs:
printf '\n%s\n' '</updates>' printf '\n%s\n' '</updates>'
} > /tmp/updates_new.xml } > /tmp/updates_new.xml
mv /tmp/updates_new.xml updates.xml mv /tmp/updates_new.xml updates.xml
} fi
fi fi
# Commit # Commit
@@ -278,8 +379,55 @@ jobs:
git push 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
# -- Mirror to GitHub (stable and rc only) --------------------------------
- name: Mirror release to GitHub
if: >-
(steps.update.outputs.stability == 'stable' || steps.update.outputs.stability == 'rc') &&
secrets.GH_TOKEN != ''
continue-on-error: true
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
run: |
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
STABILITY="${{ steps.update.outputs.stability }}"
echo "GitHub mirror sync for ${STABILITY} — ${GH_REPO}" >> $GITHUB_STEP_SUMMARY
# Mirror packages if they exist
for PKG in /tmp/*.zip /tmp/*.tar.gz; do
if [ -f "$PKG" ]; then
_RELID=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" "${GITEA_URL:-https://git.mokoconsulting.tech}/api/v1/repos/${{ github.repository }}/releases/tags/${RELEASE_TAG}" 2>/dev/null | jq -r ".id // empty")
[ -n "$_RELID" ] && curl -sf -X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" -H "Content-Type: application/octet-stream" "${GITEA_URL:-https://git.mokoconsulting.tech}/api/v1/repos/${{ github.repository }}/releases/${_RELID}/assets?name=$(basename $PKG)" --data-binary "@$PKG" > /dev/null 2>&1 || true
fi
done
- name: SFTP deploy to dev server - name: SFTP deploy to dev server
if: contains(github.ref, 'dev/') if: contains(github.ref, 'dev/') || github.ref == 'refs/heads/dev'
env: env:
DEV_HOST: ${{ vars.DEV_FTP_HOST }} DEV_HOST: ${{ vars.DEV_FTP_HOST }}
DEV_PATH: ${{ vars.DEV_FTP_PATH }} DEV_PATH: ${{ vars.DEV_FTP_PATH }}
@@ -288,15 +436,15 @@ jobs:
DEV_PORT: ${{ vars.DEV_FTP_PORT }} DEV_PORT: ${{ vars.DEV_FTP_PORT }}
DEV_KEY: ${{ secrets.DEV_FTP_KEY }} DEV_KEY: ${{ secrets.DEV_FTP_KEY }}
DEV_PASS: ${{ secrets.DEV_FTP_PASSWORD }} DEV_PASS: ${{ secrets.DEV_FTP_PASSWORD }}
GH_TOKEN: ${{ secrets.GA_TOKEN || github.token }}
run: | run: |
# ── Permission check: admin or maintain role required ────── # -- Permission check: admin or maintain role required --------
ACTOR="${{ github.actor }}" ACTOR="${{ github.actor }}"
REPO="${{ github.repository }}" REPO="${{ github.repository }}"
PERMISSION=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" "${{GITEA_URL:-https://git.mokoconsulting.tech}}/api/v1/repos/${{ github.repository }}/collaborators/${ACTOR}/permission" 2>/dev/null \ API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
2>/dev/null | jq -r '.permission' || \
curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" "${{GITEA_URL:-https://git.mokoconsulting.tech}}/api/v1/repos/${{ github.repository }}/collaborators/${ACTOR}" 2>/dev/null \ PERMISSION=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
2>/dev/null | jq -r '.role' || echo "read") "${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 case "$PERMISSION" in
admin|maintain|write) ;; admin|maintain|write) ;;
*) *)
@@ -324,11 +472,11 @@ jobs:
printf ',"password":"%s"}' "$DEV_PASS" >> /tmp/sftp-config.json printf ',"password":"%s"}' "$DEV_PASS" >> /tmp/sftp-config.json
fi fi
PLATFORM=$(php /tmp/mokostandards/api/cli/platform_detect.php --path . 2>/dev/null || true) 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 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 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 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 php /tmp/mokostandards-api/deploy/deploy-sftp.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json
fi fi
rm -f /tmp/deploy_key /tmp/sftp-config.json rm -f /tmp/deploy_key /tmp/sftp-config.json
echo "SFTP deploy to dev complete" >> $GITHUB_STEP_SUMMARY echo "SFTP deploy to dev complete" >> $GITHUB_STEP_SUMMARY

478
README.md
View File

@@ -9,7 +9,7 @@
INGROUP: MokoOnyx.Documentation INGROUP: MokoOnyx.Documentation
REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx
FILE: ./README.md FILE: ./README.md
VERSION: 01.00.25 VERSION: 01.00.19
BRIEF: Documentation for MokoOnyx template BRIEF: Documentation for MokoOnyx template
--> -->
@@ -24,489 +24,31 @@
[![Joomla](https://img.shields.io/badge/Joomla-5.x%20%7C%206.x-red.svg?logo=joomla&logoColor=white)](https://www.joomla.org) [![Joomla](https://img.shields.io/badge/Joomla-5.x%20%7C%206.x-red.svg?logo=joomla&logoColor=white)](https://www.joomla.org)
[![PHP](https://img.shields.io/badge/PHP-8.1%2B-777BB4.svg?logo=php&logoColor=white)](https://www.php.net) [![PHP](https://img.shields.io/badge/PHP-8.1%2B-777BB4.svg?logo=php&logoColor=white)](https://www.php.net)
MokoOnyx is a modern, lightweight enhancement layer built on top of Joomla's Cassiopeia template. It adds **Font Awesome 7**, **Bootstrap 5** helpers, an automatic **Table of Contents (TOC)** utility, advanced **Dark Mode** theming, and optional integrations for **Google Tag Manager** and **Google Analytics (GA4)**all while maintaining minimal core template overrides for maximum upgrade compatibility. MokoOnyx is a modern, lightweight enhancement layer built on top of Joomla's Cassiopeia template. It adds **Font Awesome 7**, **Bootstrap 5** helpers, an automatic **Table of Contents (TOC)** utility, advanced **Dark Mode** theming, and optional integrations for **Google Tag Manager** and **Google Analytics (GA4)** -- all while maintaining minimal core template overrides for maximum upgrade compatibility.
--- ## Features
## 📑 Table of Contents
- [Features](#-features)
- [Requirements](#-requirements)
- [Installation](#-installation)
- [Quick Start](#-quick-start)
- [Configuration](#-configuration)
- [Theme System](#-theme-system)
- [Development](#-development)
- [Documentation](#-documentation)
- [Changelog](#-changelog)
- [Support](#-support)
- [Contributing](#-contributing)
- [Included Libraries](#-included-libraries)
- [License](#-license)
---
## ✨ Features
### Core Enhancements
- **Built on Cassiopeia**: Extends Joomla's default template with minimal overrides - **Built on Cassiopeia**: Extends Joomla's default template with minimal overrides
- **Font Awesome 7**: Fully integrated into Joomla's asset manager with 2,000+ icons - **Font Awesome 7**: Fully integrated into Joomla's asset manager with 2,000+ icons
- **Bootstrap 5**: Extended utility classes and responsive grid system - **Bootstrap 5**: Extended utility classes and responsive grid system
- **Template Overrides**: Includes overrides for all core Joomla modules, Community Builder, and DPCalendar with consistent title rendering and Bootstrap 5 styling - **Template Overrides**: Includes overrides for all core Joomla modules, Community Builder, and DPCalendar
- **Upgrade-Friendly**: Minimal core modifications ensure smooth Joomla updates
### Advanced Theming
- **Dark Mode Support**: Built-in light/dark mode toggle with system preference detection - **Dark Mode Support**: Built-in light/dark mode toggle with system preference detection
- **Color Palettes**: Standard, Alternative, and Custom color schemes - **Google Tag Manager / GA4**: Optional analytics integrations
- **Theme Persistence**: User preferences saved via localStorage
- **Theme Control Options**: Switch, radio buttons, or hidden controls
- **Auto Dark Mode**: Optional automatic dark mode based on time/system settings
- **Meta Tags**: Automatic color-scheme and theme-color meta tags
### Developer Features
- **Custom Code Injection**: Add custom HTML to `<head>` start/end
- **Drawer Sidebars**: Configurable left/right drawer positions with custom icons
- **Font Options**: Local and web fonts (Roboto, Fira Sans, Noto Sans)
- **Sticky Header**: Optional sticky navigation
- **Back to Top**: Floating back-to-top button
### Analytics & Tracking
- **Google Tag Manager**: Optional GTM integration with container ID configuration
- **Google Analytics**: Optional GA4 integration with measurement ID
- **Privacy-Friendly**: All tracking features are optional and easily disabled
### Content Features
- **Table of Contents**: Automatic TOC generation for long articles - **Table of Contents**: Automatic TOC generation for long articles
- Placement options: `toc-left` or `toc-right` layouts
- Automatic heading extraction and navigation
- Responsive sidebar positioning
--- ## Requirements
## 📋 Requirements
- **Joomla**: 5.x or 6.x - **Joomla**: 5.x or 6.x
- **PHP**: 8.1 or higher - **PHP**: 8.1 or higher
- **Database**: MySQL 5.7+ / MariaDB 10.2+ / PostgreSQL 11+
- **Browser Support**: Modern browsers (Chrome, Firefox, Safari, Edge)
--- ## Installation
## 📦 Installation Download the latest `mokoonyx-{version}.zip` from [Releases](https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases) and install via Joomla's Extension Manager.
**Note**: MokoOnyx is a **standalone Joomla template extension** (not bundled as a package). Install it directly via Joomla's Extension Manager. ## License
### Via Joomla Extension Manager
1. Download the latest `mokoonyx-{version}.zip` from [Releases](https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases)
2. In Joomla admin, navigate to **System → Install → Extensions**
3. Upload the ZIP file and click **Upload & Install**
4. Navigate to **System → Site Templates**
5. Set **MokoOnyx** as your default template
### Via Git (Development)
```bash
git clone https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx.git
cd MokoOnyx
```
See [Development Guide](./docs/JOOMLA_DEVELOPMENT.md) for development setup.
---
## 🚀 Quick Start
### 1. Install the Template
Install `mokoonyx.zip` via Joomla's Extension Manager.
### 2. Set as Default
Navigate to **System → Site Templates** and set **MokoOnyx** as default.
### 3. Configure Template Options
Go to **System → Site Templates → MokoOnyx** to configure:
- **Branding**: Upload logo, set site title/description
- **Theme**: Configure color schemes and dark mode
- **Layout**: Set container type (static/fluid), sticky header
- **Analytics**: Add GTM/GA4 tracking codes (optional)
- **Custom Code**: Inject custom HTML/CSS/JS
### 4. Test Dark Mode
The template includes a dark mode toggle. Test it by:
- Using the floating theme toggle button (bottom-right by default)
- Checking theme persistence across page loads
- Verifying system preference detection
---
## Usage
Once installed and set as the default site template, MokoOnyx works out of the box with Joomla's standard content and module system. Key usage points:
- **Template Options** — Configure via **System → Site Templates → MokoOnyx** (theme colours, layout, analytics, favicon, drawers)
- **Custom Colour Schemes** — Copy `templates/mokoonyx/templates/light.custom.css` or `dark.custom.css` to `media/templates/site/mokoonyx/css/theme/` and select "Custom" in the Theme tab
- **Custom CSS/JS** — Create `media/templates/site/mokoonyx/css/user.css` or `js/user.js` for site-specific overrides that survive template updates
- **Module Overrides** — The template includes overrides for common Joomla modules with consistent title rendering, Bootstrap 5 styling, and Font Awesome 7 icons
- **Dark Mode** — Enabled by default with a floating toggle button; respects system preference and persists via localStorage
See [Configuration](#-configuration) below for detailed parameter reference.
---
## ⚙️ Configuration
### Global Parameters
Access template configuration via **System → Site Templates → MokoOnyx**.
#### Theme Tab
**General Settings:**
- **Theme Enabled**: Enable/disable theme system
- **Theme Control Type**: Switch (Light↔Dark), Radios (Light/Dark/System), or None
- **Default Choice**: System, Light, or Dark
- **Auto Dark Mode**: Automatic dark mode based on time
- **Meta Tags**: Enable color-scheme and theme-color meta tags
- **Bridge Bootstrap ARIA**: Sync theme with Bootstrap's data-bs-theme
**Variables & Palettes:**
- **Light Mode Palette**: Standard, Alternative, or Custom
- **Dark Mode Palette**: Standard, Alternative, or Custom
**Typography:**
- **Font Scheme**: Local (Roboto) or Web fonts (Fira Sans, Roboto+Noto Sans)
**Branding & Icons:**
- **Brand**: Enable/disable site branding
- **Logo File**: Upload custom logo (no default logo included)
- **Site Title**: Custom site title
- **Site Description**: Tagline/description
- **Font Awesome Kit**: Optional FA Pro kit code
**Header & Navigation:**
- **Sticky Header**: Enable fixed header on scroll
- **Back to Top**: Enable floating back-to-top button
**Theme Toggle UI:**
- **FAB Enabled**: Enable floating action button toggle
- **FAB Position**: Bottom-right, Bottom-left, Top-right, or Top-left
#### Advanced Tab
- **Layout**: Static or Fluid container
#### Google Tab
- **Google Tag Manager**: Enable and configure GTM container ID
- **Google Analytics**: Enable and configure GA4 measurement ID
#### Custom Code Tab
- **Custom Head Start**: HTML injected at start of `<head>`
- **Custom Head End**: HTML injected at end of `<head>`
#### Drawers Tab
- **Left Drawer Icon**: Font Awesome icon class (e.g., `fa-solid fa-chevron-right`)
- **Right Drawer Icon**: Font Awesome icon class (e.g., `fa-solid fa-chevron-left`)
### Custom Theme Palettes
MokoOnyx supports custom theme schemes:
1. **Copy template files** from `/templates/` directory:
- `light.custom.css``media/templates/site/mokoonyx/css/theme/light.custom.css`
- `dark.custom.css``media/templates/site/mokoonyx/css/theme/dark.custom.css`
2. **Customize** the CSS variables to match your brand colors
3. **Enable in Joomla**: System → Site Templates → MokoOnyx → Theme tab → Set palette to "Custom"
4. **Save** and view your site with custom colors
**Note:** Custom color files are excluded from version control (`.gitignore`) to prevent fork-specific customizations from being committed.
**Quick Example:**
```css
:root[data-bs-theme="light"] {
--color-primary: #1e40af;
--color-link: #2563eb;
--color-hover: #1d4ed8;
--body-color: #1f2937;
--body-bg: #ffffff;
}
```
**Complete Reference:** See [CSS Variables Documentation](./docs/CSS_VARIABLES.md) for all available variables and detailed usage examples.
### Table of Contents
Enable automatic TOC for articles:
1. Edit an article in Joomla admin
2. Navigate to **Options → Layout**
3. Select **toc-left** or **toc-right**
4. Save the article
The TOC will automatically generate from article headings (H2, H3, etc.) and appear as a sidebar.
---
## 🎨 Theme System
### Dark Mode Features
- **Automatic Detection**: Respects user's system preferences
- **Manual Toggle**: Floating button or radio controls
- **Persistence**: Saves preference in localStorage
- **Smooth Transitions**: Animated theme switching
- **Comprehensive Support**: All components themed for dark mode
### Theme Control Types
1. **Switch**: Simple light/dark toggle button
2. **Radios**: Three options - Light, Dark, System
3. **None**: No visible control (respects system only)
### Meta Tags
When enabled, the template adds:
```html
<meta name="color-scheme" content="light dark">
<meta name="theme-color" content="#1e3a8a" media="(prefers-color-scheme: dark)">
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
```
---
## 🛠 Development
### For Contributors
**New to the project?** See [Quick Start Guide](./docs/QUICK_START.md) for a 5-minute setup.
### Development Resources
- **[Quick Start Guide](./docs/QUICK_START.md)** - Setup and first steps
- **[Joomla Development Guide](./docs/JOOMLA_DEVELOPMENT.md)** - Testing, quality checks, deployment
- **[Workflow Guide](./docs/WORKFLOW_GUIDE.md)** - Git workflow and branching
- **[Contributing Guide](./CONTRIBUTING.md)** - Contribution guidelines
- **[Roadmap](./docs/ROADMAP.md)** - Feature roadmap and planning
### Development Tools
- **Pre-commit Hooks**: Automatic validation before commits
- **PHP CodeSniffer**: Code style validation (Joomla standards)
- **PHPStan**: Static analysis for PHP code
- **Codeception**: Testing framework
### Quick Development Setup
```bash
# Clone repository
git clone https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx.git
cd MokoOnyx
# Install development dependencies (if using Composer)
composer install --dev
# Run code quality checks
make validate # or manual commands
```
### Building Template Package
See [Joomla Development Guide](./docs/JOOMLA_DEVELOPMENT.md) for packaging instructions.
---
## 📚 Documentation
### User Documentation
- **[README](./README.md)** - This file (overview and features)
- **[CHANGELOG](./CHANGELOG.md)** - Version history and changes
- **[Roadmap](./docs/ROADMAP.md)** - Planned features and timeline
### Developer Documentation
- **[Quick Start](./docs/QUICK_START.md)** - 5-minute developer setup
- **[Development Guide](./docs/JOOMLA_DEVELOPMENT.md)** - Comprehensive development guide
- **[Workflow Guide](./docs/WORKFLOW_GUIDE.md)** - Git workflow and processes
- **[CSS Variables Reference](./docs/CSS_VARIABLES.md)** - Complete CSS customization guide
- **[Documentation Index](./docs/README.md)** - All documentation links
### Governance
- **[Contributing](./CONTRIBUTING.md)** - How to contribute
- **[Code of Conduct](./CODE_OF_CONDUCT.md)** - Community standards
- **[Governance](./GOVERNANCE.md)** - Project governance model
- **[Security Policy](./SECURITY.md)** - Security reporting and procedures
---
## 📖 Changelog
See the [CHANGELOG.md](./CHANGELOG.md) for detailed version history.
### Recent Releases
See [Gitea Releases](https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases) for all versions.
---
## 💬 Support
### Getting Help
- **Documentation**: Check this README and [docs folder](./docs/)
- **Issues**: Report bugs via [Gitea Issues](https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/issues)
- **Roadmap**: View planned features in [Roadmap](https://mokoconsulting.tech/support/joomla-cms/mokoonyx-roadmap)
### Reporting Bugs
Please include:
- Joomla version
- PHP version
- Template version
- Steps to reproduce
- Expected vs actual behavior
- Screenshots (if applicable)
### Security Issues
**Do not** report security vulnerabilities via public issues. See [SECURITY.md](./SECURITY.md) for reporting procedures.
---
## 🤝 Contributing
We welcome contributions! Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.
### How to Contribute
1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Make your changes
4. Run quality checks
5. Commit your changes (`git commit -m 'Add amazing feature'`)
6. Push to the branch (`git push origin feature/amazing-feature`)
7. Open a Pull Request
### Development Workflow
See [Workflow Guide](./docs/WORKFLOW_GUIDE.md) for detailed Git workflow.
### Customizations
For template customizations, use Joomla's built-in template settings (System → Site Templates → MokoOnyx → Custom Code tab) for HTML/CSS/JS customizations.
---
## 📦 Included Libraries
MokoOnyx includes the following third-party libraries to provide enhanced functionality:
### Bootstrap TOC
- **Version**: 1.0.1
- **Author**: Aidan Feldman
- **License**: MIT License
- **Source**: [GitHub Repository](https://github.com/afeld/bootstrap-toc)
- **Release**: [v1.0.1 Release](https://github.com/afeld/bootstrap-toc/releases/tag/v1.0.1)
- **Purpose**: Automatically generates a table of contents from article headings with scrollspy support
- **Location**: `src/media/vendor/bootstrap-toc/`
- **Integration**: Registered in `joomla.asset.json` as `vendor.bootstrap-toc` (CSS) and `vendor.bootstrap-toc.js` (JavaScript)
- **Usage**: Activated when using `toc-left` or `toc-right` article layouts
- **Features**:
- Automatic TOC generation from H1-H6 headings
- Hierarchical nested navigation
- Active state highlighting with scrollspy
- Responsive design (collapses on mobile)
- Smooth scrolling to sections
- Automatic unique ID generation for headings
- **Customizations**: CSS adapted to use MokoOnyx CSS variables for theme compatibility
### Font Awesome 7 Free
- **Version**: 7.0 (Free)
- **License**: Font Awesome Free License
- **Source**: [Font Awesome](https://fontawesome.com)
- **Purpose**: Provides 2,000+ vector icons for interface elements
- **Location**: `src/media/vendor/fa7free/`
- **Integration**: Fully integrated into Joomla's asset manager
- **Styles Available**: Solid, Regular, Brands
### Bootstrap 5
- **Version**: 5.x (via Joomla)
- **License**: MIT License
- **Source**: [Bootstrap](https://getbootstrap.com)
- **Purpose**: Provides responsive grid system and utility classes
- **Integration**: Inherited from Joomla's Cassiopeia template, extended with additional helpers
- **Components Used**: Grid, utilities, modal, dropdown, collapse, offcanvas, tooltip, popover, scrollspy
### Integration Method
All third-party libraries are:
- ✅ Properly licensed and attributed
- ✅ Registered in Joomla's Web Asset Manager (`joomla.asset.json`)
- ✅ Loaded on-demand to optimize performance
- ✅ Versioned and documented for maintenance
- ✅ Compatible with Joomla 5.x and 6.x
---
## 📄 License
This project is licensed under the **GNU General Public License v3.0** - see the [LICENSE](./LICENSE) file for details. This project is licensed under the **GNU General Public License v3.0** - see the [LICENSE](./LICENSE) file for details.
### Third-Party Licenses
- **Joomla! CMS**: GPL-2.0-or-later
- **Cassiopeia Template**: GPL-2.0-or-later (Joomla Project)
- **Font Awesome 7 Free**: Font Awesome Free License
- **Bootstrap 5**: MIT License
- **Bootstrap TOC**: MIT License (A. Feld)
All third-party libraries and assets remain the property of their respective authors and are credited in source files.
--- ---
## 🔗 Links **Made with love by [Moko Consulting](https://mokoconsulting.tech)**
- **Repository**: [Gitea](https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx)
- **Issue Tracker**: [Gitea Issues](https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/issues)
- **GitHub Mirror**: [GitHub](https://github.com/mokoconsulting-tech/MokoOnyx)
- **Roadmap**: [Full Roadmap](https://mokoconsulting.tech/support/joomla-cms/mokoonyx-roadmap)
- **Moko Consulting**: [Website](https://mokoconsulting.tech)
---
## 📊 Metadata
- **Maintainer**: Moko Consulting Engineering
- **Author**: Jonathan Miller (@jmiller-moko)
- **Repository**: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx
- **License**: GPL-3.0-or-later
- **Classification**: Public Open Source Standards
## 📝 Revision History
| Date | Version | Change Summary | Author |
| ---------- | -------- | ------------------------------------------------------------------------- | ------------------------------- |
| 2026-04-22 | 01.00.15 | Updated README: dynamic version badge, corrected requirements, fixed links | Claude Code |
| 2026-04-19 | 01.00.00 | Initial MokoOnyx release — renamed from MokoCassiopeia with auto-migration | Moko Consulting |
---
**Made with ❤️ by [Moko Consulting](https://mokoconsulting.tech)**

View File

@@ -10,8 +10,9 @@
} }
], ],
"require": { "require": {
"php": ">=8.1", "ext-zip": "*",
"ext-zip": "*" "mokoconsulting-tech/enterprise": "dev-version/04",
"php": ">=8.1"
}, },
"require-dev": { "require-dev": {
"mokoconsulting-tech/enterprise": "^4.0" "mokoconsulting-tech/enterprise": "^4.0"

144
package-lock.json generated
View File

@@ -1,144 +0,0 @@
{
"name": "mokoonyx-build",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "mokoonyx-build",
"devDependencies": {
"clean-css": "^5.3.3",
"terser": "^5.39.0"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0",
"@jridgewell/trace-mapping": "^0.3.24"
}
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/source-map": {
"version": "0.3.11",
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz",
"integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.31",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/acorn": {
"version": "8.16.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"dev": true,
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true,
"license": "MIT"
},
"node_modules/clean-css": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz",
"integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==",
"dev": true,
"license": "MIT",
"dependencies": {
"source-map": "~0.6.0"
},
"engines": {
"node": ">= 10.0"
}
},
"node_modules/commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true,
"license": "MIT"
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/source-map-support": {
"version": "0.5.21",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
"dev": true,
"license": "MIT",
"dependencies": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"node_modules/terser": {
"version": "5.46.2",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.46.2.tgz",
"integrity": "sha512-uxfo9fPcSgLDYob/w1FuL0c99MWiJDnv+5qXSQc5+Ki5NjVNsYi66INnMFBjf6uFz6OnX12piJQPF4IpjJTNTw==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
"acorn": "^8.15.0",
"commander": "^2.20.0",
"source-map-support": "~0.5.20"
},
"bin": {
"terser": "bin/terser"
},
"engines": {
"node": ">=10"
}
}
}
}

View File

@@ -25,9 +25,8 @@ $headerClass = htmlspecialchars($params->get('header_class', ''), ENT_COMPAT, 'U
if ($params->get('backgroundimage')) { if ($params->get('backgroundimage')) {
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */ /** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $app->getDocument()->getWebAssetManager(); $wa = $app->getDocument()->getWebAssetManager();
$bgUrl = Uri::root(true) . '/' . HTMLHelper::_('cleanImageURL', $params->get('backgroundimage'))->url;
$wa->addInlineStyle( $wa->addInlineStyle(
'#' . $modId . '{--hero-bg-image: url("' . $bgUrl . '"); background-image: var(--hero-bg-image);}', '#' . $modId . '{background-image: url("' . Uri::root(true) . '/' . HTMLHelper::_('cleanImageURL', $params->get('backgroundimage'))->url . '");}',
['name' => $modId] ['name' => $modId]
); );
} }

View File

@@ -101,34 +101,26 @@ form {
width: 280px; width: 280px;
} }
button.drawer-toggle-left, .drawer-toggle-left{
.drawer-toggle-left.btn {
position: fixed; position: fixed;
top: 50%; top: 50%;
left: 0px; left: 0px;
z-index: 1050; z-index: 1050;
background-color: var(--accent-color, var(--color-primary, #2a69b8)) !important; background-color: var(--nav-bg-color);
color: var(--accent-color-text, #fff) !important; color: var(--nav-text-color, gray);
border-color: var(--accent-color, var(--color-primary, #2a69b8)) !important;
padding-left: .5rem; padding-left: .5rem;
padding-right: .5rem; padding-right: .5rem;
border-top-left-radius: 0 !important;
border-bottom-left-radius: 0 !important;
} }
button.drawer-toggle-right, .drawer-toggle-right{
.drawer-toggle-right.btn {
position: fixed; position: fixed;
top: 50%; top: 50%;
right: 0px; right: 0px;
z-index: 1050; z-index: 1050;
background-color: var(--accent-color, var(--color-primary, #2a69b8)) !important; background-color: var(--nav-bg-color);
color: var(--accent-color-text, #fff) !important; color: var(--nav-text-color, gray);
border-color: var(--accent-color, var(--color-primary, #2a69b8)) !important;
padding-left: .5rem; padding-left: .5rem;
padding-right: .5rem; padding-right: .5rem;
border-top-right-radius: 0 !important;
border-bottom-right-radius: 0 !important;
} }
hr { hr {
@@ -13631,14 +13623,12 @@ a.navbar-brand {
.btn-primary { .btn-primary {
background-color: var(--color-primary, #112855); background-color: var(--color-primary, #112855);
border-color: var(--color-primary, #112855); border-color: var(--color-primary, #112855);
color: #000;
} }
.btn-primary:active, .btn-primary:active,
.btn-primary:focus { .btn-primary:focus {
background-color: var(--color-active); background-color: var(--color-active);
border-color: var(--color-active); border-color: var(--color-active);
color: #000;
} }
.btn-group { .btn-group {
@@ -13935,22 +13925,13 @@ meter {
} }
} }
/* ── HERO WRAPPER (module wrapper, full-width centering container) ── */ /* ── HERO CARD BASE ── */
.hero { .hero {
display: flex; display: flex;
justify-content: var(--hero-justify, center); justify-content: center;
align-items: var(--hero-align, center); align-items: center;
width: 100%; width: 100%;
min-height: var(--hero-min-height, 75vh); min-height: var(--banner-min-height, 60vh);
background-image: var(--hero-bg-image, none);
background-repeat: var(--hero-bg-repeat, no-repeat);
background-attachment: var(--hero-bg-attachment, fixed);
background-position: var(--hero-bg-position, center);
background-size: var(--hero-bg-size, cover);
}
/* ── HERO CARD BASE (the .card inside the hero wrapper) ── */
.hero .card {
max-width: var(--hero-card-max-width, 600px); max-width: var(--hero-card-max-width, 600px);
padding: var(--hero-card-padding-y, 3rem) var(--hero-card-padding-x, 2rem); padding: var(--hero-card-padding-y, 3rem) var(--hero-card-padding-x, 2rem);
background-color: var(--hero-card-bg, var(--hero-primary-bg-color, #0d1e3a)); background-color: var(--hero-card-bg, var(--hero-primary-bg-color, #0d1e3a));
@@ -13959,21 +13940,23 @@ meter {
background-position: center; background-position: center;
color: var(--hero-card-color, var(--hero-primary-color, #f1f5f9)); color: var(--hero-card-color, var(--hero-primary-color, #f1f5f9));
border-radius: var(--hero-card-border-radius, .5rem); border-radius: var(--hero-card-border-radius, .5rem);
text-align: var(--hero-card-text-align, center);
margin-left: auto;
margin-right: auto;
border: none;
} }
/* ── PRIMARY VARIANT ── */ .hero .card {
.hero#primary .card { margin-left: auto;
margin-right: auto;
text-align: center;
}
/* ── PRIMARY VARIANT (uses default card vars) ── */
.hero#primary {
background-color: var(--hero-card-bg, #0d1e3a); background-color: var(--hero-card-bg, #0d1e3a);
background-image: var(--hero-card-overlay, none); background-image: var(--hero-card-overlay, none);
color: var(--hero-card-color, #f1f5f9); color: var(--hero-card-color, #f1f5f9);
} }
/* ── SECONDARY / ALTERNATIVE VARIANT ── */ /* ── SECONDARY / ALTERNATIVE VARIANT ── */
.hero#secondary .card { .hero#secondary {
max-width: var(--hero-alt-card-max-width, 600px); max-width: var(--hero-alt-card-max-width, 600px);
padding: var(--hero-alt-card-padding-y, 3rem) var(--hero-alt-card-padding-x, 2rem); padding: var(--hero-alt-card-padding-y, 3rem) var(--hero-alt-card-padding-x, 2rem);
background-color: var(--hero-alt-card-bg, #080f1e); background-color: var(--hero-alt-card-bg, #080f1e);
@@ -13995,14 +13978,14 @@ meter {
background-image: none; background-image: none;
} }
.hero { .hero {
min-height: 100vh;
min-height: 100dvh;
flex-direction: column;
}
.hero .card {
max-width: 100%; max-width: 100%;
width: 100%; width: 100%;
min-height: 100vh;
min-height: 100dvh;
border-radius: 0; border-radius: 0;
display: flex;
flex-direction: column;
justify-content: center;
} }
} }
@@ -16988,14 +16971,11 @@ body:not(.has-sidebar-right) .site-grid .container-component {
} }
.btn-primary { .btn-primary {
--bs-btn-color: #000;
--bs-btn-bg: #333; --bs-btn-bg: #333;
--bs-btn-border-color: #333; --bs-btn-border-color: #333;
--bs-btn-hover-color: #000;
--bs-btn-hover-bg: #555; --bs-btn-hover-bg: #555;
--bs-btn-hover-border-color: #555; --bs-btn-hover-border-color: #555;
--bs-btn-focus-shadow-rgb: 49, 132, 253; --bs-btn-focus-shadow-rgb: 49, 132, 253;
--bs-btn-active-color: #000;
--bs-btn-active-bg: #555; --bs-btn-active-bg: #555;
--bs-btn-active-border-color: #555; --bs-btn-active-border-color: #555;
--bs-btn-disabled-bg: #A0A0A0; --bs-btn-disabled-bg: #A0A0A0;
@@ -18981,7 +18961,7 @@ nav[data-toggle=toc] .nav-link.active+ul{
/* ===== BOOTSTRAP & JOOMLA BUTTONS ===== */ /* ===== BOOTSTRAP & JOOMLA BUTTONS ===== */
.btn-primary { .btn-primary {
--btn-color: #000; --btn-color: white;
--btn-bg: var(--color-primary, #112855); --btn-bg: var(--color-primary, #112855);
--btn-border-color: var(--color-primary, #112855); --btn-border-color: var(--color-primary, #112855);
--btn-hover-bg: color-mix(in srgb, var(--color-primary, #112855) 85%, black); --btn-hover-bg: color-mix(in srgb, var(--color-primary, #112855) 85%, black);

View File

@@ -39,13 +39,13 @@
</server> </server>
</updateservers> </updateservers>
<name>MokoOnyx</name> <name>MokoOnyx</name>
<version>01.00.25</version> <version>01.00.18</version>
<scriptfile>script.php</scriptfile> <scriptfile>script.php</scriptfile>
<creationDate>2026-04-15</creationDate> <creationDate>2026-04-15</creationDate>
<author>Jonathan Miller || Moko Consulting</author> <author>Jonathan Miller || Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail> <authorEmail>hello@mokoconsulting.tech</authorEmail>
<copyright>(C)GNU General Public License Version 3 - 2026 Moko Consulting</copyright> <copyright>(C)GNU General Public License Version 3 - 2026 Moko Consulting</copyright>
<description><![CDATA[<div style="padding:1rem;border:2px solid #059669;border-radius:8px;background:#ecfdf5;margin-bottom:1rem;"> <h3 style="color:#065f46;margin:0 0 .5rem;">Migrating from MokoCassiopeia?</h3> <p style="margin:0 0 .75rem;">MokoOnyx automatically imports your MokoCassiopeia settings on first use. To trigger the migration:</p> <ol style="margin:0 0 .75rem 1.5rem;"> <li>Install MokoOnyx via <strong>System → Install → Extensions</strong></li> <li>Go to <strong>System → Site Templates</strong> and set <strong>MokoOnyx</strong> as your default template</li> <li><strong>Visit any page on your site</strong> — the migration runs automatically on first page load</li> <li>Check <strong>administrator/logs/mokoonyx_migrate.log.php</strong> to confirm migration completed</li> <li>Uninstall MokoCassiopeia from <strong>Extensions → Manage</strong></li> </ol> <p style="margin:0;font-size:.9em;color:#065f46;"> <strong>What gets migrated:</strong> template style params, custom colour palettes (light.custom.css, dark.custom.css), user.css, user.js, and update server URL. To re-run the migration, delete <code>templates/mokoonyx/.migrated</code> and reload any page. </p></div> <p><img src="https://img.shields.io/gitea/v/release/MokoConsulting/MokoOnyx?gitea_url=https%3A%2F%2Fgit.mokoconsulting.tech&amp;logo=gitea&amp;logoColor=white&amp;label=version" alt="Version" /> <img src="https://img.shields.io/badge/license-GPL--3.0--or--later-green.svg?logo=gnu&amp;logoColor=white" alt="License" /> <img src="https://img.shields.io/badge/Joomla-5.x%20%7C%206.x-red.svg?logo=joomla&amp;logoColor=white" alt="Joomla" /> <img src="https://img.shields.io/badge/PHP-8.1%2B-777BB4.svg?logo=php&amp;logoColor=white" alt="PHP" /></p> <h3>MokoOnyx</h3> <p> <strong>MokoOnyx</strong> (formerly MokoCassiopeia) is a modern, lightweight enhancement layer built on Joomla's Cassiopeia template. It adds Font Awesome 7, Bootstrap 5 helpers, automatic Table of Contents, advanced Dark Mode theming, and optional Google Tag Manager / GA4 integration — all with minimal core overrides for maximum upgrade compatibility. </p> <h4>Custom Colour Themes</h4> <p> Copy <code>templates/mokoonyx/templates/light.custom.css</code> to <code>media/templates/site/mokoonyx/css/theme/light.custom.css</code> (or dark equivalent), customise the CSS variables, then select "Custom" in the Theme tab. </p> <h4>Custom CSS &amp; JavaScript</h4> <ul> <li><code>media/templates/site/mokoonyx/css/user.css</code> — custom CSS overrides</li> <li><code>media/templates/site/mokoonyx/js/user.js</code> — custom JavaScript</li> </ul> <p>These files survive template updates.</p> <h4>CSS Variables Reference</h4> <p>For a complete list of CSS variables, see the <a href="https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx" target="_blank">MokoOnyx repository documentation</a>.</p>]]></description> <description><![CDATA[<div style="padding:1rem;border:2px solid #059669;border-radius:8px;background:#ecfdf5;margin-bottom:1rem;"> <h3 style="color:#065f46;margin:0 0 .5rem;">Migrating from MokoCassiopeia?</h3> <p style="margin:0 0 .75rem;">MokoOnyx automatically imports your MokoCassiopeia settings on first use. To trigger the migration:</p> <ol style="margin:0 0 .75rem 1.5rem;"> <li>Install MokoOnyx via <strong>System → Install → Extensions</strong></li> <li>Go to <strong>System → Site Templates</strong> and set <strong>MokoOnyx</strong> as your default template</li> <li><strong>Visit any page on your site</strong> — the migration runs automatically on first page load</li> <li>Check <strong>administrator/logs/mokoonyx_migrate.log.php</strong> to confirm migration completed</li> <li>Uninstall MokoCassiopeia from <strong>Extensions → Manage</strong></li> </ol> <p style="margin:0;font-size:.9em;color:#065f46;"> <strong>What gets migrated:</strong> template style params, custom colour palettes (light.custom.css, dark.custom.css), user.css, user.js, and update server URL. To re-run the migration, delete <code>templates/mokoonyx/.migrated</code> and reload any page. </p></div> <p><img src="https://img.shields.io/gitea/v/release/MokoConsulting/MokoOnyx?gitea_url=https%3A%2F%2Fgit.mokoconsulting.tech&amp;logo=gitea&amp;logoColor=white&amp;label=version" alt="Version" /> <img src="https://img.shields.io/badge/license-GPL--3.0--or--later-green.svg?logo=gnu&amp;logoColor=white" alt="License" /> <img src="https://img.shields.io/badge/Joomla-5.x%20%7C%206.x-red.svg?logo=joomla&amp;logoColor=white" alt="Joomla" /> <img src="https://img.shields.io/badge/PHP-8.1%2B-777BB4.svg?logo=php&amp;logoColor=white" alt="PHP" /></p> <h3>MokoOnyx</h3> <p> <strong>MokoOnyx</strong> (formerly MokoCassiopeia) is a modern, lightweight enhancement layer built on Joomla's Cassiopeia template. It adds Font Awesome 7, Bootstrap 5 helpers, automatic Table of Contents, advanced Dark Mode theming, and optional Google Tag Manager / GA4 integration — all with minimal core overrides for maximum upgrade compatibility. </p> <h4>Custom Colour Themes</h4> <p> Copy <code>templates/mokoonyx/templates/light.custom.css</code> to <code>media/templates/site/mokoonyx/css/theme/light.custom.css</code> (or dark equivalent), customise the CSS variables, then select "Custom" in the Theme tab. </p> <h4>Custom CSS &amp; JavaScript</h4> <ul> <li><code>media/templates/site/mokoonyx/css/user.css</code> — custom CSS overrides</li> <li><code>media/templates/site/mokoonyx/js/user.js</code> — custom JavaScript</li> </ul> <p>These files survive template updates.</p>]]></description>
<inheritable>1</inheritable> <inheritable>1</inheritable>
<files> <files>
<filename>component.php</filename> <filename>component.php</filename>
@@ -327,11 +327,51 @@
</field> </field>
</fieldset> </fieldset>
<!-- CSS Variables: see repo documentation --> <!-- CSS Variables reference tab -->
<fieldset name="css_variables" label="TPL_MOKOONYX_CSS_VARS_FIELDSET_LABEL"> <fieldset name="css_variables" label="TPL_MOKOONYX_CSS_VARS_FIELDSET_LABEL">
<field name="css_vars_docs_link" type="note" <field name="css_vars_intro" type="note" description="TPL_MOKOONYX_CSS_VARS_INTRO" />
description="For a full list of CSS variables and theming options, see the &lt;a href='https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx' target='_blank'&gt;MokoOnyx documentation&lt;/a&gt;." <field name="css_vars_brand" type="note" label="TPL_MOKOONYX_CSS_VARS_BRAND_LABEL" description="TPL_MOKOONYX_CSS_VARS_BRAND_DESC" class="alert alert-light" />
class="alert alert-info" /> <field name="css_vars_links" type="note" label="TPL_MOKOONYX_CSS_VARS_LINKS_LABEL" description="TPL_MOKOONYX_CSS_VARS_LINKS_DESC" class="alert alert-light" />
<field name="css_vars_typography" type="note" label="TPL_MOKOONYX_CSS_VARS_TYPO_LABEL" description="TPL_MOKOONYX_CSS_VARS_TYPO_DESC" class="alert alert-light" />
<field name="css_vars_nav" type="note" label="TPL_MOKOONYX_CSS_VARS_NAV_LABEL" description="TPL_MOKOONYX_CSS_VARS_NAV_DESC" class="alert alert-light" />
<field name="css_vars_layout" type="note" label="TPL_MOKOONYX_CSS_VARS_LAYOUT_LABEL" description="TPL_MOKOONYX_CSS_VARS_LAYOUT_DESC" class="alert alert-light" />
<field name="css_vars_breakpoints" type="note" label="TPL_MOKOONYX_CSS_VARS_BP_LABEL" description="TPL_MOKOONYX_CSS_VARS_BP_DESC" class="alert alert-light" />
<field name="css_vars_bootstrap" type="note" label="TPL_MOKOONYX_CSS_VARS_BS_LABEL" description="TPL_MOKOONYX_CSS_VARS_BS_DESC" class="alert alert-light" />
<field name="css_vars_bootstrap_states" type="note" label="TPL_MOKOONYX_CSS_VARS_BS_STATES_LABEL" description="TPL_MOKOONYX_CSS_VARS_BS_STATES_DESC" class="alert alert-light" />
<field name="css_vars_alert_list" type="note" label="TPL_MOKOONYX_CSS_VARS_ALERT_LIST_LABEL" description="TPL_MOKOONYX_CSS_VARS_ALERT_LIST_DESC" class="alert alert-light" />
<field name="css_vars_colors" type="note" label="TPL_MOKOONYX_CSS_VARS_COLORS_LABEL" description="TPL_MOKOONYX_CSS_VARS_COLORS_DESC" class="alert alert-light" />
<field name="css_vars_hero" type="note" label="TPL_MOKOONYX_CSS_VARS_HERO_LABEL" description="TPL_MOKOONYX_CSS_VARS_HERO_DESC" class="alert alert-light" />
<field name="css_vars_hero_variants" type="note" label="TPL_MOKOONYX_CSS_VARS_HERO_VARIANTS_LABEL" description="TPL_MOKOONYX_CSS_VARS_HERO_VARIANTS_DESC" class="alert alert-info" />
<field name="css_vars_block_colors" type="note" label="TPL_MOKOONYX_CSS_VARS_BLOCK_COLORS_LABEL" description="TPL_MOKOONYX_CSS_VARS_BLOCK_COLORS_DESC" class="alert alert-info" />
<field name="css_vars_header" type="note" label="TPL_MOKOONYX_CSS_VARS_HEADER_LABEL" description="TPL_MOKOONYX_CSS_VARS_HEADER_DESC" class="alert alert-light" />
<field name="css_vars_containers" type="note" label="TPL_MOKOONYX_CSS_VARS_CONTAINERS_LABEL" description="TPL_MOKOONYX_CSS_VARS_CONTAINERS_DESC" class="alert alert-light" />
<field name="css_vars_borders" type="note" label="TPL_MOKOONYX_CSS_VARS_BORDERS_LABEL" description="TPL_MOKOONYX_CSS_VARS_BORDERS_DESC" class="alert alert-light" />
<field name="css_vars_shadows" type="note" label="TPL_MOKOONYX_CSS_VARS_SHADOWS_LABEL" description="TPL_MOKOONYX_CSS_VARS_SHADOWS_DESC" class="alert alert-light" />
<field name="css_vars_forms" type="note" label="TPL_MOKOONYX_CSS_VARS_FORMS_LABEL" description="TPL_MOKOONYX_CSS_VARS_FORMS_DESC" class="alert alert-light" />
<field name="css_vars_buttons" type="note" label="TPL_MOKOONYX_CSS_VARS_BUTTONS_LABEL" description="TPL_MOKOONYX_CSS_VARS_BUTTONS_DESC" class="alert alert-light" />
<field name="css_vars_cards" type="note" label="TPL_MOKOONYX_CSS_VARS_CARDS_LABEL" description="TPL_MOKOONYX_CSS_VARS_CARDS_DESC" class="alert alert-light" />
<field name="css_vars_accordion" type="note" label="TPL_MOKOONYX_CSS_VARS_ACCORDION_LABEL" description="TPL_MOKOONYX_CSS_VARS_ACCORDION_DESC" class="alert alert-light" />
<field name="css_vars_alert_base" type="note" label="TPL_MOKOONYX_CSS_VARS_ALERT_BASE_LABEL" description="TPL_MOKOONYX_CSS_VARS_ALERT_BASE_DESC" class="alert alert-light" />
<field name="css_vars_badge" type="note" label="TPL_MOKOONYX_CSS_VARS_BADGE_LABEL" description="TPL_MOKOONYX_CSS_VARS_BADGE_DESC" class="alert alert-light" />
<field name="css_vars_breadcrumb" type="note" label="TPL_MOKOONYX_CSS_VARS_BREADCRUMB_LABEL" description="TPL_MOKOONYX_CSS_VARS_BREADCRUMB_DESC" class="alert alert-light" />
<field name="css_vars_dropdown_menu" type="note" label="TPL_MOKOONYX_CSS_VARS_DROPDOWN_MENU_LABEL" description="TPL_MOKOONYX_CSS_VARS_DROPDOWN_MENU_DESC" class="alert alert-light" />
<field name="css_vars_list_group" type="note" label="TPL_MOKOONYX_CSS_VARS_LIST_GROUP_LABEL" description="TPL_MOKOONYX_CSS_VARS_LIST_GROUP_DESC" class="alert alert-light" />
<field name="css_vars_modal" type="note" label="TPL_MOKOONYX_CSS_VARS_MODAL_LABEL" description="TPL_MOKOONYX_CSS_VARS_MODAL_DESC" class="alert alert-light" />
<field name="css_vars_backdrop" type="note" label="TPL_MOKOONYX_CSS_VARS_BACKDROP_LABEL" description="TPL_MOKOONYX_CSS_VARS_BACKDROP_DESC" class="alert alert-light" />
<field name="css_vars_nav_tabs" type="note" label="TPL_MOKOONYX_CSS_VARS_NAV_TABS_LABEL" description="TPL_MOKOONYX_CSS_VARS_NAV_TABS_DESC" class="alert alert-light" />
<field name="css_vars_nav_pills" type="note" label="TPL_MOKOONYX_CSS_VARS_NAV_PILLS_LABEL" description="TPL_MOKOONYX_CSS_VARS_NAV_PILLS_DESC" class="alert alert-light" />
<field name="css_vars_pagination" type="note" label="TPL_MOKOONYX_CSS_VARS_PAGINATION_LABEL" description="TPL_MOKOONYX_CSS_VARS_PAGINATION_DESC" class="alert alert-light" />
<field name="css_vars_popover" type="note" label="TPL_MOKOONYX_CSS_VARS_POPOVER_LABEL" description="TPL_MOKOONYX_CSS_VARS_POPOVER_DESC" class="alert alert-light" />
<field name="css_vars_progress" type="note" label="TPL_MOKOONYX_CSS_VARS_PROGRESS_LABEL" description="TPL_MOKOONYX_CSS_VARS_PROGRESS_DESC" class="alert alert-light" />
<field name="css_vars_spinner" type="note" label="TPL_MOKOONYX_CSS_VARS_SPINNER_LABEL" description="TPL_MOKOONYX_CSS_VARS_SPINNER_DESC" class="alert alert-light" />
<field name="css_vars_table" type="note" label="TPL_MOKOONYX_CSS_VARS_TABLE_LABEL" description="TPL_MOKOONYX_CSS_VARS_TABLE_DESC" class="alert alert-light" />
<field name="css_vars_toast" type="note" label="TPL_MOKOONYX_CSS_VARS_TOAST_LABEL" description="TPL_MOKOONYX_CSS_VARS_TOAST_DESC" class="alert alert-light" />
<field name="css_vars_tooltip" type="note" label="TPL_MOKOONYX_CSS_VARS_TOOLTIP_LABEL" description="TPL_MOKOONYX_CSS_VARS_TOOLTIP_DESC" class="alert alert-light" />
<field name="css_vars_components" type="note" label="TPL_MOKOONYX_CSS_VARS_COMPONENTS_LABEL" description="TPL_MOKOONYX_CSS_VARS_COMPONENTS_DESC" class="alert alert-light" />
<field name="css_vars_offcanvas" type="note" label="TPL_MOKOONYX_CSS_VARS_OFFCANVAS_LABEL" description="TPL_MOKOONYX_CSS_VARS_OFFCANVAS_DESC" class="alert alert-light" />
<field name="css_vars_virtuemart" type="note" label="TPL_MOKOONYX_CSS_VARS_VM_LABEL" description="TPL_MOKOONYX_CSS_VARS_VM_DESC" class="alert alert-light" />
<field name="css_vars_gable" type="note" label="TPL_MOKOONYX_CSS_VARS_GABLE_LABEL" description="TPL_MOKOONYX_CSS_VARS_GABLE_DESC" class="alert alert-light" />
<field name="css_vars_footer" type="note" label="TPL_MOKOONYX_CSS_VARS_FOOTER_LABEL" description="TPL_MOKOONYX_CSS_VARS_FOOTER_DESC" class="alert alert-light" />
</fieldset> </fieldset>
</fields> </fields>
</config> </config>

View File

@@ -97,13 +97,13 @@
<element>mokoonyx</element> <element>mokoonyx</element>
<type>template</type> <type>template</type>
<client>site</client> <client>site</client>
<version>01.00.20</version> <version>01.00.25</version>
<creationDate>2026-04-23</creationDate> <creationDate>2026-04-23</creationDate>
<infourl title='MokoOnyx'>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/stable</infourl> <infourl title='MokoOnyx'>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/stable</infourl>
<downloads> <downloads>
<downloadurl type='full' format='zip'>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/stable/mokoonyx-01.00.20.zip</downloadurl> <downloadurl type='full' format='zip'>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/stable/mokoonyx-01.00.25.zip</downloadurl>
</downloads> </downloads>
<sha256>0e490ad750ba4fd65eebb8412de6a17e94872fde63be0827f7b8679d400d8130</sha256> <sha256>d111a027bdd5f1eae1fe973933fa4043be2740d62fea3527fb856275f8fb3be4</sha256>
<tags><tag>stable</tag></tags> <tags><tag>stable</tag></tags>
<maintainer>Moko Consulting</maintainer> <maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl> <maintainerurl>https://mokoconsulting.tech</maintainerurl>