Compare commits
121 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d026ce727d | |||
| 431fe41fd9 | |||
| 000fbdb24d | |||
| 2377aac953 | |||
| c1c9ea3ce3 | |||
| e74b053931 | |||
| 2cfe54b85d | |||
| 5670850258 | |||
| 8d1468ffae | |||
| f21e9169e3 | |||
| a3c3b56655 | |||
| f18fb7ce4a | |||
| 9c8487492c | |||
| 4f56bb0563 | |||
| 3cbeb7c96e | |||
| d727fefa57 | |||
| 1b6dafa88a | |||
| 7d8c112cb7 | |||
| d5436a58f4 | |||
| 06468b4393 | |||
| a4c6d59f35 | |||
| 61823554d4 | |||
| ae3992fe44 | |||
| b7d1e5407f | |||
| d4a8858226 | |||
| c52f7e7624 | |||
| 3d4596bad7 | |||
| eb1e5eaa24 | |||
| af25f3ead2 | |||
| e538ec6348 | |||
| 5729a2334c | |||
| d730ce8deb | |||
| 3aaa22ecce | |||
| d8e6938021 | |||
| e621b4d901 | |||
| 8263cbd5c9 | |||
| 346f031075 | |||
| f95ac86875 | |||
| 6dce03b3ed | |||
| 23d0affc18 | |||
| c110c2ceca | |||
| 22dcdb90df | |||
| c0e1cebe18 | |||
| 17b2a0d7be | |||
| 8002c6cddf | |||
| 6495a74f75 | |||
| 277082114f | |||
| 25054e1273 | |||
| 6fa45b8c98 | |||
| b2bf5dda7e | |||
| 1c56349833 | |||
| 07e904a0b4 | |||
| 48b3e8099b | |||
| 5c8859ac29 | |||
| 5c3600b358 | |||
| 569979c1b8 | |||
| 159ad30ec7 | |||
| 1d807ca7a8 | |||
| f76b24de13 | |||
| 00a4fd7b46 | |||
| 79ce5f4a49 | |||
| d5e2018540 | |||
| 37317288cc | |||
| 797abc928a | |||
| e6543251d8 | |||
| 6f940be6d2 | |||
| 9aa01cccc5 | |||
| 13f29a0df8 | |||
| 70e8f621f7 | |||
| 2b370497d1 | |||
| f6aef1959e | |||
| b53bba53ac | |||
| 9c9db876d0 | |||
| 7d2ea3e04f | |||
| 130bd2c05f | |||
| 654b27925c | |||
| 708c70de33 | |||
| f383462d05 | |||
| 6810cdc537 | |||
| a293ac6a69 | |||
| e3e4f351c6 | |||
| e83f5631fb | |||
| 3d0e1f91fe | |||
| 5f1599d64c | |||
| 7922ca3e67 | |||
| e2871bc5c6 | |||
| 0bb0b99701 | |||
| be47d213f3 | |||
| 1e353f5519 | |||
| 4929cb0d0e | |||
| 9d04e22852 | |||
| ec859bcc06 | |||
| e321e0f824 | |||
| b7de171730 | |||
| 4150b26712 | |||
| c9a7627d29 | |||
| d30698b826 | |||
| 6f808fe91d | |||
| 42cd991bf4 | |||
| 19ac79b3e7 | |||
| 7fe73aced2 | |||
| 192e1a0b0b | |||
| ec812da933 | |||
| 4beb399689 | |||
| 5bf23b3f21 | |||
| d5dd5f316d | |||
| 19178728b9 | |||
| fa5b0a5163 | |||
| e78cffa0a9 | |||
| a6bba8559c | |||
| 6034b4b150 | |||
| 6125f945be | |||
| 2b0f0a3704 | |||
| f26ddb1ef8 | |||
| 257ecb688a | |||
| 583af12dce | |||
| f73563d6ee | |||
| f83fca9e31 | |||
| 4ef655bce3 | |||
| a1ce2f91a9 | |||
| 4365a5ab4a |
@@ -5,9 +5,10 @@
|
||||
-->
|
||||
<moko-platform xmlns="https://standards.mokoconsulting.tech/moko-platform/1.0" schema-version="1.0">
|
||||
<identity>
|
||||
<name>MokoOnyx</name>
|
||||
<name>Template - MokoOnyx</name>
|
||||
<org>MokoConsulting</org>
|
||||
<description>MokoOnyx - Joomla site template (successor to MokoCassiopeia)</description>
|
||||
<version>02.08.00</version>
|
||||
<license spdx="GPL-3.0-or-later">GNU General Public License v3</license>
|
||||
</identity>
|
||||
<governance>
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: moko-platform.Release
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
# PATH: /.mokogitea/workflows/auto-bump.yml
|
||||
# VERSION: 09.02.00
|
||||
# BRIEF: Auto patch-bump version on every push to dev (skips merge commits)
|
||||
|
||||
name: "Universal: Auto Version Bump"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
bump:
|
||||
name: Version Bump
|
||||
runs-on: release
|
||||
if: >-
|
||||
!contains(github.event.head_commit.message, '[skip ci]') &&
|
||||
!contains(github.event.head_commit.message, '[skip bump]') &&
|
||||
!startsWith(github.event.head_commit.message, 'Merge pull request')
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup moko-platform tools
|
||||
run: |
|
||||
if ! command -v composer &> /dev/null; then
|
||||
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1
|
||||
fi
|
||||
if [ -d "/opt/moko-platform/cli" ]; then
|
||||
echo "MOKO_CLI=/opt/moko-platform/cli" >> "$GITHUB_ENV"
|
||||
else
|
||||
git clone --depth 1 --branch main --quiet \
|
||||
"https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/moko-platform.git" \
|
||||
/tmp/moko-platform-api
|
||||
cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet
|
||||
echo "MOKO_CLI=/tmp/moko-platform-api/cli" >> "$GITHUB_ENV"
|
||||
fi
|
||||
|
||||
- name: Bump version
|
||||
run: |
|
||||
BUMP=$(php ${MOKO_CLI}/version_bump.php --path . 2>&1) || true
|
||||
echo "$BUMP"
|
||||
|
||||
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null) || true
|
||||
[ -z "$VERSION" ] && { echo "No version found — skipping"; exit 0; }
|
||||
|
||||
# Propagate to platform manifests with -dev suffix
|
||||
php ${MOKO_CLI}/version_set_platform.php \
|
||||
--path . --version "$VERSION" --branch dev --stability dev 2>/dev/null || true
|
||||
php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
|
||||
VERSION="${VERSION}-dev"
|
||||
|
||||
# Commit if anything changed
|
||||
if git diff --quiet && git diff --cached --quiet; then
|
||||
echo "No version changes to commit"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "gitea-actions[bot]"
|
||||
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||
git add -A
|
||||
git commit -m "chore(version): auto-bump patch ${VERSION} [skip ci]" \
|
||||
--author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>"
|
||||
git push origin dev
|
||||
echo "Bumped to ${VERSION}" >> $GITHUB_STEP_SUMMARY
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,48 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: MokoStandards.Universal
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
# PATH: /.mokogitea/workflows/branch-cleanup.yml
|
||||
# VERSION: 01.00.00
|
||||
# BRIEF: Delete feature branches after PR merge
|
||||
|
||||
name: "Branch Cleanup"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
jobs:
|
||||
cleanup:
|
||||
name: Delete merged branch
|
||||
runs-on: ubuntu-latest
|
||||
if: >-
|
||||
github.event.pull_request.merged == true &&
|
||||
github.event.pull_request.head.ref != 'dev' &&
|
||||
github.event.pull_request.head.ref != 'main'
|
||||
|
||||
steps:
|
||||
- name: Delete source branch
|
||||
run: |
|
||||
BRANCH="${{ github.event.pull_request.head.ref }}"
|
||||
API="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}/api/v1/repos/${{ github.repository }}/branches"
|
||||
ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('${BRANCH}', safe=''))")
|
||||
|
||||
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X DELETE \
|
||||
-H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||
"${API}/${ENCODED}" 2>/dev/null || true)
|
||||
|
||||
if [ "$STATUS" = "204" ]; then
|
||||
echo "Deleted branch: ${BRANCH}" >> $GITHUB_STEP_SUMMARY
|
||||
elif [ "$STATUS" = "404" ]; then
|
||||
echo "Branch already deleted: ${BRANCH}" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "::warning::Failed to delete branch ${BRANCH} (HTTP ${STATUS})"
|
||||
fi
|
||||
@@ -8,21 +8,21 @@
|
||||
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform
|
||||
# PATH: /templates/workflows/cascade-dev.yml.template
|
||||
# VERSION: 02.00.00
|
||||
# BRIEF: Forward-merge main -> all open branches after every push to main
|
||||
# BRIEF: Forward-merge main → all open branches after every push to main
|
||||
#
|
||||
# +========================================================================+
|
||||
# | CASCADE MAIN -> ALL BRANCHES |
|
||||
# | CASCADE MAIN → ALL BRANCHES |
|
||||
# +========================================================================+
|
||||
# | |
|
||||
# | Triggers on every push to main (PR merges, bot commits, etc.) |
|
||||
# | |
|
||||
# | 1. List all branches matching: dev, rc/*, beta/*, alpha/* |
|
||||
# | 2. For each: create PR (main -> branch), auto-merge if clean |
|
||||
# | 2. For each: create PR (main → branch), auto-merge if clean |
|
||||
# | 3. On conflict: leave PR open for manual resolution |
|
||||
# | |
|
||||
# +========================================================================+
|
||||
|
||||
name: "Universal: Cascade Main -> Dev"
|
||||
name: "Universal: Cascade Main → Dev"
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -42,7 +42,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
cascade:
|
||||
name: Cascade main -> branches
|
||||
name: Cascade main → branches
|
||||
runs-on: ubuntu-latest
|
||||
if: >-
|
||||
!contains(github.event.head_commit.message, '[skip ci]') &&
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
- name: Discover target branches
|
||||
id: branches
|
||||
env:
|
||||
GA_TOKEN: ${{ secrets.GA_TOKEN }}
|
||||
GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
run: |
|
||||
API="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
ALL_BRANCHES=""
|
||||
while true; do
|
||||
BATCH=$(curl -sS \
|
||||
-H "Authorization: token ${GA_TOKEN}" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
"${API}/branches?page=${PAGE}&limit=50" \
|
||||
| jq -r '.[].name // empty')
|
||||
[ -z "$BATCH" ] && break
|
||||
@@ -83,17 +83,17 @@ jobs:
|
||||
|
||||
if [ -z "$TARGETS" ]; then
|
||||
echo "targets=" >> "$GITHUB_OUTPUT"
|
||||
echo " No cascade target branches found"
|
||||
echo "ℹ️ No cascade target branches found"
|
||||
else
|
||||
echo "targets=$TARGETS" >> "$GITHUB_OUTPUT"
|
||||
COUNT=$(echo "$TARGETS" | wc -w)
|
||||
echo " Found ${COUNT} target branch(es): ${TARGETS}"
|
||||
echo "📋 Found ${COUNT} target branch(es): ${TARGETS}"
|
||||
fi
|
||||
|
||||
- name: Cascade to all target branches
|
||||
if: steps.branches.outputs.targets != ''
|
||||
env:
|
||||
GA_TOKEN: ${{ secrets.GA_TOKEN }}
|
||||
GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
run: |
|
||||
API="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
SHORT_SHA="${GITHUB_SHA:0:7}"
|
||||
@@ -106,27 +106,27 @@ jobs:
|
||||
|
||||
for BRANCH in $TARGETS; do
|
||||
echo ""
|
||||
echo " main -> ${BRANCH} "
|
||||
echo "═══ main → ${BRANCH} ═══"
|
||||
|
||||
# Check if branch is already up to date
|
||||
ENCODED_BRANCH=$(echo "$BRANCH" | sed 's|/|%2F|g')
|
||||
RESPONSE=$(curl -sS \
|
||||
-H "Authorization: token ${GA_TOKEN}" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
"${API}/compare/${ENCODED_BRANCH}...main")
|
||||
|
||||
AHEAD=$(echo "$RESPONSE" | jq '.total_commits // 0')
|
||||
|
||||
if [ "$AHEAD" -eq 0 ]; then
|
||||
echo " Already up to date"
|
||||
echo " ✅ Already up to date"
|
||||
SKIPPED=$((SKIPPED + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
echo " main is ${AHEAD} commit(s) ahead"
|
||||
echo " ℹ️ main is ${AHEAD} commit(s) ahead"
|
||||
|
||||
# Check for existing cascade PR
|
||||
EXISTING=$(curl -sS \
|
||||
-H "Authorization: token ${GA_TOKEN}" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
"${API}/pulls?state=open&head=${GITEA_ORG}:main&base=${ENCODED_BRANCH}&limit=1")
|
||||
|
||||
EXISTING_COUNT=$(echo "$EXISTING" | jq 'length')
|
||||
@@ -134,16 +134,16 @@ jobs:
|
||||
|
||||
if [ "$EXISTING_COUNT" -gt 0 ]; then
|
||||
PR_NUMBER=$(echo "$EXISTING" | jq -r '.[0].number')
|
||||
echo " Reusing existing PR #${PR_NUMBER}"
|
||||
echo " ℹ️ Reusing existing PR #${PR_NUMBER}"
|
||||
else
|
||||
# Create cascade PR
|
||||
PR_RESPONSE=$(curl -sS -w "\n%{http_code}" \
|
||||
-X POST \
|
||||
-H "Authorization: token ${GA_TOKEN}" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"title\": \"chore: cascade main -> ${BRANCH} (${SHORT_SHA}) [skip ci]\",
|
||||
\"body\": \"## Automatic cascade\\n\\nForward-merging \`main\` (${SHORT_SHA}) into \`${BRANCH}\`.\\n\\nIf conflicts exist, resolve manually and merge.\\n\\n> Auto-created by **Cascade Main -> Dev**.\",
|
||||
\"title\": \"chore: cascade main → ${BRANCH} (${SHORT_SHA}) [skip ci]\",
|
||||
\"body\": \"## Automatic cascade\\n\\nForward-merging \`main\` (${SHORT_SHA}) into \`${BRANCH}\`.\\n\\nIf conflicts exist, resolve manually and merge.\\n\\n> Auto-created by **Cascade Main → Dev**.\",
|
||||
\"head\": \"main\",
|
||||
\"base\": \"${BRANCH}\"
|
||||
}" \
|
||||
@@ -155,34 +155,34 @@ jobs:
|
||||
|
||||
if [ "$HTTP_CODE" != "201" ] || [ -z "$PR_NUMBER" ]; then
|
||||
MSG=$(echo "$BODY" | jq -r '.message // .' 2>/dev/null | head -1)
|
||||
echo " Failed to create PR (HTTP ${HTTP_CODE}): ${MSG}"
|
||||
echo " ❌ Failed to create PR (HTTP ${HTTP_CODE}): ${MSG}"
|
||||
FAILED=$((FAILED + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
echo " Created PR #${PR_NUMBER}"
|
||||
echo " ✅ Created PR #${PR_NUMBER}"
|
||||
fi
|
||||
|
||||
# Try auto-merge
|
||||
PR_DATA=$(curl -sS \
|
||||
-H "Authorization: token ${GA_TOKEN}" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
"${API}/pulls/${PR_NUMBER}")
|
||||
|
||||
MERGEABLE=$(echo "$PR_DATA" | jq -r '.mergeable // false')
|
||||
|
||||
if [ "$MERGEABLE" != "true" ]; then
|
||||
echo " Conflicts -- PR #${PR_NUMBER} left open"
|
||||
echo " ⚠️ Conflicts — PR #${PR_NUMBER} left open"
|
||||
CONFLICTS=$((CONFLICTS + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
MERGE_RESPONSE=$(curl -sS -w "\n%{http_code}" \
|
||||
-X POST \
|
||||
-H "Authorization: token ${GA_TOKEN}" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"Do\": \"merge\",
|
||||
\"merge_message_field\": \"chore: cascade main -> ${BRANCH} [skip ci]\",
|
||||
\"merge_message_field\": \"chore: cascade main → ${BRANCH} [skip ci]\",
|
||||
\"delete_branch_after_merge\": false
|
||||
}" \
|
||||
"${API}/pulls/${PR_NUMBER}/merge")
|
||||
@@ -190,23 +190,23 @@ jobs:
|
||||
MERGE_HTTP=$(echo "$MERGE_RESPONSE" | tail -1)
|
||||
|
||||
if [ "$MERGE_HTTP" = "200" ] || [ "$MERGE_HTTP" = "204" ]; then
|
||||
echo " Merged -- ${BRANCH} is in sync"
|
||||
echo " ✅ Merged — ${BRANCH} is in sync"
|
||||
SUCCESS=$((SUCCESS + 1))
|
||||
else
|
||||
MERGE_BODY=$(echo "$MERGE_RESPONSE" | sed '$d')
|
||||
echo " Merge failed (HTTP ${MERGE_HTTP}) -- PR #${PR_NUMBER} left open"
|
||||
echo " ⚠️ Merge failed (HTTP ${MERGE_HTTP}) — PR #${PR_NUMBER} left open"
|
||||
CONFLICTS=$((CONFLICTS + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
echo ""
|
||||
echo " Merged: ${SUCCESS}"
|
||||
echo " Conflicts: ${CONFLICTS}"
|
||||
echo " Up to date: ${SKIPPED}"
|
||||
echo " Failed: ${FAILED}"
|
||||
echo ""
|
||||
echo "════════════════════════════════════════"
|
||||
echo " ✅ Merged: ${SUCCESS}"
|
||||
echo " ⚠️ Conflicts: ${CONFLICTS}"
|
||||
echo " ⏭️ Up to date: ${SKIPPED}"
|
||||
echo " ❌ Failed: ${FAILED}"
|
||||
echo "════════════════════════════════════════"
|
||||
|
||||
if [ "$FAILED" -gt 0 ]; then
|
||||
exit 1
|
||||
|
||||
@@ -43,9 +43,9 @@ jobs:
|
||||
|
||||
- name: Clone MokoStandards
|
||||
env:
|
||||
GA_TOKEN: ${{ secrets.GA_TOKEN || secrets.GA_TOKEN || github.token }}
|
||||
MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN || secrets.GA_TOKEN || github.token }}
|
||||
MOKO_CLONE_HOST: ${{ secrets.GA_TOKEN && 'git.mokoconsulting.tech/MokoConsulting' || 'github.com/mokoconsulting-tech' }}
|
||||
GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN || secrets.MOKOGITEA_TOKEN || github.token }}
|
||||
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN || secrets.MOKOGITEA_TOKEN || github.token }}
|
||||
MOKO_CLONE_HOST: ${{ secrets.MOKOGITEA_TOKEN && 'git.mokoconsulting.tech/MokoConsulting' || 'github.com/mokoconsulting-tech' }}
|
||||
run: |
|
||||
git clone --depth 1 --branch main --quiet \
|
||||
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/MokoStandards-API.git" \
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
|
||||
- name: Install dependencies
|
||||
env:
|
||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GA_TOKEN || github.token }}"}}'
|
||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.MOKOGITEA_TOKEN || github.token }}"}}'
|
||||
run: |
|
||||
if [ -f "composer.json" ]; then
|
||||
composer install \
|
||||
@@ -346,7 +346,7 @@ jobs:
|
||||
|
||||
- name: Install dependencies
|
||||
env:
|
||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GA_TOKEN || github.token }}"}}'
|
||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.MOKOGITEA_TOKEN || github.token }}"}}'
|
||||
run: |
|
||||
if [ -f "composer.json" ]; then
|
||||
composer install \
|
||||
@@ -391,7 +391,7 @@ jobs:
|
||||
|
||||
- name: Install dependencies
|
||||
env:
|
||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GA_TOKEN || github.token }}"}}'
|
||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.MOKOGITEA_TOKEN || github.token }}"}}'
|
||||
run: |
|
||||
if [ -f "composer.json" ]; then
|
||||
composer install --no-interaction --prefer-dist --optimize-autoloader
|
||||
@@ -448,3 +448,20 @@ jobs:
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
exit $EXIT
|
||||
|
||||
pre-release:
|
||||
name: Build RC Pre-Release
|
||||
runs-on: ubuntu-latest
|
||||
needs: [lint-and-validate, test]
|
||||
if: github.event_name == 'pull_request'
|
||||
|
||||
steps:
|
||||
- name: Trigger pre-release build
|
||||
env:
|
||||
GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
BRANCH: ${{ github.head_ref }}
|
||||
run: |
|
||||
curl -s -X POST "${GITEA_URL:-https://git.mokoconsulting.tech}/api/v1/repos/${REPO}/actions/workflows/pre-release.yml/dispatches" -H "Authorization: token ${GITEA_TOKEN}" -H "Content-Type: application/json" -d "{\"ref\":\"${BRANCH}\",\"inputs\":{\"stability\":\"release-candidate\"}}"
|
||||
echo "### Pre-Release" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Triggered RC build on branch \`${BRANCH}\`" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
# PATH: /.gitea/workflows/cleanup.yml
|
||||
# VERSION: 01.00.00
|
||||
# BRIEF: Scheduled cleanup -- delete merged branches and old workflow runs
|
||||
# BRIEF: Scheduled cleanup — delete merged branches and old workflow runs
|
||||
|
||||
name: "Universal: Repository Cleanup"
|
||||
|
||||
@@ -33,17 +33,17 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GA_TOKEN }}
|
||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
|
||||
- name: Delete merged branches
|
||||
env:
|
||||
GA_TOKEN: ${{ secrets.GA_TOKEN }}
|
||||
GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
run: |
|
||||
echo "=== Merged Branch Cleanup ==="
|
||||
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
|
||||
|
||||
# List branches via API
|
||||
BRANCHES=$(curl -sS -H "Authorization: token ${GA_TOKEN}" \
|
||||
BRANCHES=$(curl -sS -H "Authorization: token ${GITEA_TOKEN}" \
|
||||
"${API}/branches?limit=50" | jq -r '.[].name')
|
||||
|
||||
DELETED=0
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
# Check if branch is merged into main
|
||||
if git merge-base --is-ancestor "origin/${BRANCH}" origin/main 2>/dev/null; then
|
||||
echo " Deleting merged branch: ${BRANCH}"
|
||||
curl -sS -X DELETE -H "Authorization: token ${GA_TOKEN}" \
|
||||
curl -sS -X DELETE -H "Authorization: token ${GITEA_TOKEN}" \
|
||||
"${API}/branches/${BRANCH}" 2>/dev/null || true
|
||||
DELETED=$((DELETED + 1))
|
||||
fi
|
||||
@@ -66,20 +66,20 @@ jobs:
|
||||
|
||||
- name: Clean old workflow runs
|
||||
env:
|
||||
GA_TOKEN: ${{ secrets.GA_TOKEN }}
|
||||
GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
run: |
|
||||
echo "=== Workflow Run Cleanup ==="
|
||||
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
|
||||
CUTOFF=$(date -d "30 days ago" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -v-30d +%Y-%m-%dT%H:%M:%SZ)
|
||||
|
||||
# Get old completed runs
|
||||
RUNS=$(curl -sS -H "Authorization: token ${GA_TOKEN}" \
|
||||
RUNS=$(curl -sS -H "Authorization: token ${GITEA_TOKEN}" \
|
||||
"${API}/actions/runs?status=completed&limit=50" | \
|
||||
jq -r ".workflow_runs[] | select(.created_at < \"${CUTOFF}\") | .id" 2>/dev/null)
|
||||
|
||||
DELETED=0
|
||||
for RUN_ID in $RUNS; do
|
||||
curl -sS -X DELETE -H "Authorization: token ${GA_TOKEN}" \
|
||||
curl -sS -X DELETE -H "Authorization: token ${GITEA_TOKEN}" \
|
||||
"${API}/actions/runs/${RUN_ID}" 2>/dev/null || true
|
||||
DELETED=$((DELETED + 1))
|
||||
done
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform
|
||||
# PATH: /templates/workflows/gitleaks.yml.template
|
||||
# VERSION: 01.00.00
|
||||
# BRIEF: Secret scanning -- detect leaked credentials, API keys, and tokens
|
||||
# BRIEF: Secret scanning — detect leaked credentials, API keys, and tokens
|
||||
#
|
||||
# +========================================================================+
|
||||
# | SECRET SCANNING |
|
||||
@@ -89,7 +89,7 @@ jobs:
|
||||
run: |
|
||||
REPO="${{ github.event.repository.name }}"
|
||||
curl -sS \
|
||||
-H "Title: ${REPO} -- secrets detected in code" \
|
||||
-H "Title: ${REPO} — secrets detected in code" \
|
||||
-H "Tags: rotating_light,key" \
|
||||
-H "Priority: urgent" \
|
||||
-d "Gitleaks found potential secrets. Review and rotate credentials immediately." \
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: moko-platform.Automation
|
||||
# VERSION: 01.00.00
|
||||
# BRIEF: Auto-create feature branch when an issue is opened
|
||||
|
||||
name: "Universal: Issue Branch"
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
|
||||
env:
|
||||
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
|
||||
jobs:
|
||||
create-branch:
|
||||
name: Create feature branch
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Create branch and comment
|
||||
run: |
|
||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
|
||||
ISSUE_NUM="${{ github.event.issue.number }}"
|
||||
ISSUE_TITLE="${{ github.event.issue.title }}"
|
||||
|
||||
# Build slug from title: lowercase, replace non-alnum with dash, trim
|
||||
SLUG=$(echo "${ISSUE_TITLE}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//' | cut -c1-40)
|
||||
BRANCH="feature/${ISSUE_NUM}-${SLUG}"
|
||||
|
||||
# Check dev branch exists
|
||||
DEV_EXISTS=$(curl -sf -o /dev/null -w '%{http_code}' \
|
||||
-H "Authorization: token ${TOKEN}" \
|
||||
"${API}/branches/dev" 2>/dev/null || echo "000")
|
||||
|
||||
if [ "${DEV_EXISTS}" != "200" ]; then
|
||||
echo "No dev branch -- skipping"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Create branch from dev
|
||||
HTTP=$(curl -sf -o /dev/null -w '%{http_code}' -X POST \
|
||||
-H "Authorization: token ${TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API}/branches" \
|
||||
-d "{\"new_branch_name\":\"${BRANCH}\",\"old_branch_name\":\"dev\"}" 2>/dev/null || echo "000")
|
||||
|
||||
if [ "${HTTP}" = "201" ]; then
|
||||
echo "Created branch: ${BRANCH}"
|
||||
|
||||
# Comment on issue with branch link
|
||||
REPO_URL="${GITEA_URL}/${{ github.repository }}"
|
||||
BODY="Branch created: [\`${BRANCH}\`](${REPO_URL}/src/branch/${BRANCH})\n\n\`\`\`bash\ngit fetch origin\ngit checkout ${BRANCH}\n\`\`\`"
|
||||
|
||||
curl -sf -X POST \
|
||||
-H "Authorization: token ${TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API}/issues/${ISSUE_NUM}/comments" \
|
||||
-d "{\"body\":\"${BODY}\"}" > /dev/null 2>&1
|
||||
|
||||
echo "Commented on issue #${ISSUE_NUM}"
|
||||
else
|
||||
echo "Failed to create branch (HTTP ${HTTP}) -- may already exist"
|
||||
fi
|
||||
@@ -8,7 +8,7 @@
|
||||
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform
|
||||
# PATH: /templates/workflows/universal/pr-check.yml.template
|
||||
# VERSION: 05.00.00
|
||||
# BRIEF: PR gate -- branch policy + code validation before merge
|
||||
# BRIEF: PR gate — branch policy + code validation before merge
|
||||
|
||||
name: "Universal: PR Check"
|
||||
|
||||
@@ -24,7 +24,7 @@ env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
jobs:
|
||||
# Branch Policy
|
||||
# ── Branch Policy ──────────────────────────────────────────────────────
|
||||
branch-policy:
|
||||
name: Branch Policy
|
||||
runs-on: ubuntu-latest
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
HEAD="${{ github.head_ref }}"
|
||||
BASE="${{ github.base_ref }}"
|
||||
|
||||
echo "PR: ${HEAD} -> ${BASE}"
|
||||
echo "PR: ${HEAD} → ${BASE}"
|
||||
|
||||
ALLOWED=true
|
||||
REASON=""
|
||||
@@ -85,18 +85,18 @@ jobs:
|
||||
echo "${REASON}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "### Allowed merge paths:" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`feature/*\` -> \`dev\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`fix/*\` -> \`dev\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`hotfix/*\` -> \`dev\` or \`main\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`dev\` -> \`main\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`rc/*\` -> \`main\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`feature/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`fix/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`hotfix/*\` → \`dev\` or \`main\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`dev\` → \`main\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`rc/*\` → \`main\`" >> $GITHUB_STEP_SUMMARY
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Branch policy: OK (${HEAD} -> ${BASE})"
|
||||
echo "Branch policy: OK (${HEAD} → ${BASE})"
|
||||
echo "## Branch Policy: Passed" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Code Validation
|
||||
# ── Code Validation ────────────────────────────────────────────────────
|
||||
validate:
|
||||
name: Validate PR
|
||||
runs-on: ubuntu-latest
|
||||
@@ -162,7 +162,7 @@ jobs:
|
||||
echo "Dolibarr module: ${MOD_FILE}"
|
||||
;;
|
||||
*)
|
||||
echo "Generic platform -- no manifest validation"
|
||||
echo "Generic platform — no manifest validation"
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -194,3 +194,21 @@ jobs:
|
||||
FILE_COUNT=$(find "$SOURCE_DIR" -type f | wc -l)
|
||||
echo "Source: ${FILE_COUNT} files"
|
||||
[ "$FILE_COUNT" -gt 0 ] || { echo "::error::Source directory is empty"; exit 1; }
|
||||
|
||||
# ── Pre-Release RC Build ─────────────────────────────────────────────────
|
||||
pre-release:
|
||||
name: Build RC Package
|
||||
runs-on: ubuntu-latest
|
||||
needs: [branch-policy, validate]
|
||||
|
||||
steps:
|
||||
- name: Trigger RC pre-release
|
||||
env:
|
||||
GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
BRANCH: ${{ github.head_ref }}
|
||||
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
run: |
|
||||
curl -s -X POST "${GITEA_URL}/api/v1/repos/${REPO}/actions/workflows/pre-release.yml/dispatches" -H "Authorization: token ${GITEA_TOKEN}" -H "Content-Type: application/json" -d "{\"ref\":\"${BRANCH}\",\"inputs\":{\"stability\":\"release-candidate\"}}"
|
||||
echo "### Pre-Release" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Triggered RC build on branch \`${BRANCH}\`" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
@@ -5,14 +5,18 @@
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: moko-platform.Release
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
# PATH: /templates/workflows/universal/pre-release.yml.template
|
||||
# VERSION: 05.00.00
|
||||
# VERSION: 05.01.00
|
||||
# BRIEF: Manual pre-release -- builds dev/alpha/beta/rc packages from any branch
|
||||
|
||||
name: "Universal: Pre-Release"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
branches:
|
||||
- dev
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
stability:
|
||||
@@ -35,41 +39,44 @@ env:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: "Build Pre-Release (${{ inputs.stability }})"
|
||||
name: "Build Pre-Release (${{ inputs.stability || 'development' }})"
|
||||
runs-on: release
|
||||
if: >-
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
(github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev')
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GA_TOKEN }}
|
||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
|
||||
- name: Setup PHP
|
||||
- name: Setup moko-platform tools
|
||||
env:
|
||||
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
||||
run: |
|
||||
if ! command -v php &> /dev/null; then
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip >/dev/null 2>&1
|
||||
if ! command -v composer &> /dev/null; then
|
||||
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1
|
||||
fi
|
||||
# Always fetch latest CLI tools — never use stale cache from previous runs
|
||||
rm -rf /tmp/moko-platform-api
|
||||
git clone --depth 1 --branch main --quiet \
|
||||
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
|
||||
/tmp/moko-platform-api
|
||||
cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet
|
||||
echo "MOKO_CLI=/tmp/moko-platform-api/cli" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Detect platform
|
||||
id: platform
|
||||
run: |
|
||||
tr -d '[:space:]')| tr -d '[:space:]')
|
||||
[ -z "$PLATFORM" ] && PLATFORM="generic"
|
||||
echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT"
|
||||
# For packages: prefer pkg_*.xml in src/; fallback to any manifest
|
||||
MANIFEST=$(find ./src -maxdepth 1 -name "pkg_*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
|
||||
[ -z "$MANIFEST" ] && MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" ! -path "*/packages/*" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
|
||||
[ -z "$MANIFEST" ] && MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
|
||||
MOD_FILE=$(find . -maxdepth 4 -name "mod*.class.php" ! -path "./.git/*" -exec grep -l 'extends DolibarrModules' {} \; 2>/dev/null | head -1)
|
||||
echo "manifest=${MANIFEST}" >> "$GITHUB_OUTPUT"
|
||||
echo "mod_file=${MOD_FILE}" >> "$GITHUB_OUTPUT"
|
||||
php ${MOKO_CLI}/manifest_read.php --path . --github-output
|
||||
|
||||
- name: Resolve metadata
|
||||
- name: Resolve metadata and bump version
|
||||
id: meta
|
||||
run: |
|
||||
STABILITY="${{ inputs.stability }}"
|
||||
STABILITY="${{ inputs.stability || 'development' }}"
|
||||
|
||||
case "$STABILITY" in
|
||||
development) SUFFIX="-dev"; TAG="development" ;;
|
||||
@@ -78,109 +85,44 @@ jobs:
|
||||
release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;;
|
||||
esac
|
||||
|
||||
# Read and bump patch version (with rollover)
|
||||
CURRENT=$(sed -n 's/.*VERSION:[[:space:]]*\([0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\).*/\1/p' README.md 2>/dev/null | head -1)
|
||||
[ -z "$CURRENT" ] && CURRENT="00.00.00"
|
||||
# Read current version (bump already handled by push workflow)
|
||||
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null)
|
||||
[ -z "$VERSION" ] && VERSION="00.00.01"
|
||||
|
||||
MAJOR=$(echo "$CURRENT" | cut -d. -f1)
|
||||
MINOR=$(echo "$CURRENT" | cut -d. -f2)
|
||||
PATCH=$(echo "$CURRENT" | cut -d. -f3)
|
||||
# Strip any existing suffix from version before applying stability
|
||||
VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//')
|
||||
|
||||
# Patch bump with rollover: ZZ=99 -> bump minor, YY=99 -> bump major
|
||||
NEW_PATCH=$((10#$PATCH + 1))
|
||||
NEW_MINOR=$((10#$MINOR))
|
||||
NEW_MAJOR=$((10#$MAJOR))
|
||||
php ${MOKO_CLI}/version_set_platform.php \
|
||||
--path . --version "$VERSION" --branch "${{ github.ref_name }}" --stability "$STABILITY" 2>/dev/null || true
|
||||
|
||||
if [ $NEW_PATCH -gt 99 ]; then
|
||||
NEW_PATCH=0
|
||||
NEW_MINOR=$((NEW_MINOR + 1))
|
||||
# Verify version consistency across all files
|
||||
php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
|
||||
|
||||
# Update VERSION variable with suffix
|
||||
if [ -n "$SUFFIX" ]; then
|
||||
VERSION="${VERSION}${SUFFIX}"
|
||||
fi
|
||||
if [ $NEW_MINOR -gt 99 ]; then
|
||||
NEW_MINOR=0
|
||||
NEW_MAJOR=$((NEW_MAJOR + 1))
|
||||
fi
|
||||
|
||||
VERSION=$(printf "%02d.%02d.%02d" $NEW_MAJOR $NEW_MINOR $NEW_PATCH)
|
||||
TODAY=$(date +%Y-%m-%d)
|
||||
|
||||
echo "Bumping: ${CURRENT} -> ${VERSION} (patch)"
|
||||
|
||||
# Update README.md
|
||||
sed -i "s/VERSION:[[:space:]]*${CURRENT}/VERSION: ${VERSION}/" README.md
|
||||
|
||||
# Update platform-specific manifest
|
||||
PLATFORM="${{ steps.platform.outputs.platform }}"
|
||||
MANIFEST="${{ steps.platform.outputs.manifest }}"
|
||||
MOD_FILE="${{ steps.platform.outputs.mod_file }}"
|
||||
case "$PLATFORM" in
|
||||
joomla)
|
||||
if [ -n "$MANIFEST" ]; then
|
||||
MANIFEST_VER=$(sed -n 's/.*<version>\([^<]*\)<\/version>.*/\1/p' "$MANIFEST" | head -1)
|
||||
sed -i "s|<version>${MANIFEST_VER}</version>|<version>${VERSION}</version>|" "$MANIFEST"
|
||||
sed -i "s|<creationDate>[^<]*</creationDate>|<creationDate>${TODAY}</creationDate>|" "$MANIFEST"
|
||||
fi
|
||||
# For packages: also bump version in all sub-extension manifests
|
||||
if [ -d "src/packages" ]; then
|
||||
for SUB_MANIFEST in $(find src/packages -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null); do
|
||||
SUB_VER=$(sed -n 's/.*<version>\([^<]*\)<\/version>.*/\1/p' "$SUB_MANIFEST" | head -1)
|
||||
if [ -n "$SUB_VER" ]; then
|
||||
sed -i "s|<version>${SUB_VER}</version>|<version>${VERSION}</version>|" "$SUB_MANIFEST"
|
||||
sed -i "s|<creationDate>[^<]*</creationDate>|<creationDate>${TODAY}</creationDate>|" "$SUB_MANIFEST"
|
||||
echo " Bumped sub-extension: $(basename $SUB_MANIFEST) ${SUB_VER} -> ${VERSION}"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
;;
|
||||
dolibarr)
|
||||
if [ -n "$MOD_FILE" ]; then
|
||||
sed -i "s/\$this->version = '[^']*'/\$this->version = '${VERSION}'/" "$MOD_FILE"
|
||||
fi
|
||||
;;
|
||||
*) ;;
|
||||
esac
|
||||
|
||||
# Commit version bump
|
||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "gitea-actions[bot]"
|
||||
git remote set-url origin "https://jmiller:${{ secrets.GA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||
git add -A
|
||||
git diff --cached --quiet || {
|
||||
git commit -m "chore(version): bump ${CURRENT} -> ${VERSION} [skip ci]"
|
||||
git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]"
|
||||
git push origin HEAD 2>&1
|
||||
}
|
||||
|
||||
# Auto-detect element (platform-aware)
|
||||
case "$PLATFORM" in
|
||||
joomla)
|
||||
MANIFEST="${{ steps.platform.outputs.manifest }}"
|
||||
EXT_ELEMENT=""
|
||||
if [ -n "$MANIFEST" ]; then
|
||||
EXT_ELEMENT=$(sed -n 's/.*<element>\([^<]*\)<\/element>.*/\1/p' "$MANIFEST" 2>/dev/null | head -1)
|
||||
if [ -z "$EXT_ELEMENT" ]; then
|
||||
EXT_ELEMENT=$(basename "$MANIFEST" .xml | tr '[:upper:]' '[:lower:]')
|
||||
case "$EXT_ELEMENT" in
|
||||
templatedetails|manifest) EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') ;;
|
||||
esac
|
||||
fi
|
||||
else
|
||||
EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
|
||||
fi
|
||||
;;
|
||||
dolibarr)
|
||||
MOD_FILE="${{ steps.platform.outputs.mod_file }}"
|
||||
if [ -n "$MOD_FILE" ]; then
|
||||
MOD_BASENAME=$(basename "$MOD_FILE" .class.php)
|
||||
EXT_ELEMENT=$(echo "$MOD_BASENAME" | sed 's/^mod//' | tr '[:upper:]' '[:lower:]')
|
||||
else
|
||||
EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
|
||||
;;
|
||||
esac
|
||||
# Auto-detect element via manifest_element.php
|
||||
php ${MOKO_CLI}/manifest_element.php \
|
||||
--path . --version "$VERSION" --stability "$STABILITY" \
|
||||
--repo "${GITEA_REPO}" --github-output
|
||||
|
||||
ZIP_NAME="${EXT_ELEMENT}-${VERSION}${SUFFIX}.zip"
|
||||
# Read back element outputs
|
||||
EXT_ELEMENT=$(grep '^ext_element=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
|
||||
ZIP_NAME=$(grep '^zip_name=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
|
||||
[ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
|
||||
[ -z "$ZIP_NAME" ] && ZIP_NAME="${EXT_ELEMENT}-${VERSION}.zip"
|
||||
|
||||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT"
|
||||
@@ -188,183 +130,52 @@ jobs:
|
||||
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
|
||||
echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT"
|
||||
echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT"
|
||||
echo "manifest=${MANIFEST}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ==="
|
||||
|
||||
- name: Build package
|
||||
run: |
|
||||
SOURCE_DIR="src"
|
||||
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
|
||||
if [ ! -d "$SOURCE_DIR" ]; then
|
||||
echo "::error::No src/ or htdocs/ directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MANIFEST="${{ steps.meta.outputs.manifest }}"
|
||||
EXT_TYPE=""
|
||||
if [ -n "$MANIFEST" ]; then
|
||||
EXT_TYPE=$(sed -n 's/.*<extension[^>]*type="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
|
||||
fi
|
||||
|
||||
EXCLUDES="sftp-config* .ftpignore *.ppk *.pem *.key .env* *.local .build-trigger"
|
||||
|
||||
mkdir -p build/package
|
||||
|
||||
if [ "$EXT_TYPE" = "package" ] && [ -d "${SOURCE_DIR}/packages" ]; then
|
||||
echo "=== Building Joomla PACKAGE (multi-extension) ==="
|
||||
|
||||
# 1) ZIP each sub-extension in src/packages/
|
||||
for ext_dir in "${SOURCE_DIR}"/packages/*/; do
|
||||
[ ! -d "$ext_dir" ] && continue
|
||||
EXT_NAME=$(basename "$ext_dir")
|
||||
echo " Packaging sub-extension: ${EXT_NAME}"
|
||||
cd "$ext_dir"
|
||||
zip -r "../../build/package/${EXT_NAME}.zip" . -x $EXCLUDES
|
||||
cd "$OLDPWD"
|
||||
done
|
||||
|
||||
# 2) Copy package-level files (manifest, script, etc.)
|
||||
for f in "${SOURCE_DIR}"/*.xml "${SOURCE_DIR}"/*.php; do
|
||||
[ -f "$f" ] && cp "$f" build/package/
|
||||
done
|
||||
|
||||
echo "Package contents:"
|
||||
ls -la build/package/
|
||||
else
|
||||
echo "=== Building standard Joomla extension ==="
|
||||
rsync -a \
|
||||
--exclude='sftp-config*' \
|
||||
--exclude='.ftpignore' \
|
||||
--exclude='*.ppk' \
|
||||
--exclude='*.pem' \
|
||||
--exclude='*.key' \
|
||||
--exclude='.env*' \
|
||||
--exclude='*.local' \
|
||||
--exclude='.build-trigger' \
|
||||
"${SOURCE_DIR}/" build/package/
|
||||
fi
|
||||
|
||||
- name: Create ZIP
|
||||
id: zip
|
||||
run: |
|
||||
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
||||
cd build/package
|
||||
zip -r "../${ZIP_NAME}" .
|
||||
cd ..
|
||||
|
||||
SHA256=$(sha256sum "${ZIP_NAME}" | cut -d' ' -f1)
|
||||
echo "sha256=${SHA256}" >> "$GITHUB_OUTPUT"
|
||||
echo "ZIP: ${ZIP_NAME} (SHA: ${SHA256:0:16}...)"
|
||||
|
||||
- name: Create or replace Gitea release
|
||||
- name: Create release
|
||||
id: release
|
||||
run: |
|
||||
TAG="${{ steps.meta.outputs.tag }}"
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||
SHA256="${{ steps.zip.outputs.sha256 }}"
|
||||
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
||||
EXT_ELEMENT="${{ steps.meta.outputs.ext_element }}"
|
||||
TOKEN="${{ secrets.GA_TOKEN }}"
|
||||
API="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
BRANCH=$(git branch --show-current)
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
php ${MOKO_CLI}/release_create.php \
|
||||
--path . --version "$VERSION" --tag "$TAG" \
|
||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
||||
--repo "${GITEA_REPO}" --branch dev --prerelease
|
||||
|
||||
BODY="## ${VERSION} ($(date +%Y-%m-%d))
|
||||
**Channel:** ${STABILITY}
|
||||
**SHA-256:** \`${SHA256}\`"
|
||||
|
||||
# Delete existing release
|
||||
EXISTING_ID=$(curl -sS -H "Authorization: token ${TOKEN}" \
|
||||
"${API}/releases/tags/${TAG}" | jq -r '.id // empty' 2>/dev/null)
|
||||
if [ -n "$EXISTING_ID" ]; then
|
||||
curl -sS -X DELETE -H "Authorization: token ${TOKEN}" \
|
||||
"${API}/releases/${EXISTING_ID}" 2>/dev/null || true
|
||||
curl -sS -X DELETE -H "Authorization: token ${TOKEN}" \
|
||||
"${API}/tags/${TAG}" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Create release
|
||||
RELEASE_ID=$(curl -sS -X POST -H "Authorization: token ${TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API}/releases" \
|
||||
-d "$(jq -n \
|
||||
--arg tag "$TAG" \
|
||||
--arg target "$BRANCH" \
|
||||
--arg name "${EXT_ELEMENT} ${VERSION} (${STABILITY})" \
|
||||
--arg body "$BODY" \
|
||||
'{tag_name: $tag, target_commitish: $target, name: $name, body: $body, prerelease: true}'
|
||||
)" | jq -r '.id')
|
||||
|
||||
echo "release_id=${RELEASE_ID}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# Upload ZIP
|
||||
curl -sS -X POST -H "Authorization: token ${TOKEN}" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
"${API}/releases/${RELEASE_ID}/assets?name=${ZIP_NAME}" \
|
||||
--data-binary "@build/${ZIP_NAME}"
|
||||
|
||||
echo "Released: ${EXT_ELEMENT} ${VERSION} (${STABILITY})"
|
||||
- name: Build package and upload
|
||||
id: package
|
||||
run: |
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
TAG="${{ steps.meta.outputs.tag }}"
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
php ${MOKO_CLI}/release_package.php \
|
||||
--path . --version "$VERSION" --tag "$TAG" \
|
||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
||||
--repo "${GITEA_REPO}" --output /tmp || true
|
||||
|
||||
- name: Update updates.xml
|
||||
if: steps.platform.outputs.platform == 'joomla'
|
||||
run: |
|
||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
SHA256="${{ steps.zip.outputs.sha256 }}"
|
||||
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
||||
TAG="${{ steps.meta.outputs.tag }}"
|
||||
DATE=$(date +%Y-%m-%d)
|
||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||
SHA256="${{ steps.package.outputs.sha256_zip }}"
|
||||
|
||||
if [ ! -f "updates.xml" ]; then
|
||||
echo "No updates.xml -- skipping"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
export PY_STABILITY="$STABILITY" PY_VERSION="$VERSION" PY_SHA256="$SHA256" \
|
||||
PY_ZIP_NAME="$ZIP_NAME" PY_TAG="$TAG" PY_DATE="$DATE" \
|
||||
PY_GITEA_ORG="$GITEA_ORG" PY_GITEA_REPO="$GITEA_REPO"
|
||||
python3 << 'PYEOF'
|
||||
import re, os
|
||||
SHA_FLAG=""
|
||||
[ -n "$SHA256" ] && SHA_FLAG="--sha ${SHA256}"
|
||||
|
||||
stability = os.environ["PY_STABILITY"]
|
||||
version = os.environ["PY_VERSION"]
|
||||
sha256 = os.environ["PY_SHA256"]
|
||||
zip_name = os.environ["PY_ZIP_NAME"]
|
||||
tag = os.environ["PY_TAG"]
|
||||
date = os.environ["PY_DATE"]
|
||||
gitea_org = os.environ["PY_GITEA_ORG"]
|
||||
gitea_repo = os.environ["PY_GITEA_REPO"]
|
||||
download_url = f"https://git.mokoconsulting.tech/{gitea_org}/{gitea_repo}/releases/download/{tag}/{zip_name}"
|
||||
php ${MOKO_CLI}/updates_xml_build.php \
|
||||
--path . --version "${VERSION}" --stability "${STABILITY}" \
|
||||
--gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \
|
||||
${SHA_FLAG}
|
||||
|
||||
with open("updates.xml", "r") as f:
|
||||
content = f.read()
|
||||
|
||||
# Map stability to XML tag name
|
||||
tag_map = {"development": "development", "alpha": "alpha", "beta": "beta", "release-candidate": "rc"}
|
||||
xml_tag = tag_map.get(stability, stability)
|
||||
|
||||
pattern = r"(<update>(?:(?!</update>).)*?<tag>" + re.escape(xml_tag) + r"</tag>.*?</update>)"
|
||||
match = re.search(pattern, content, re.DOTALL)
|
||||
if match:
|
||||
block = match.group(1)
|
||||
updated = re.sub(r"<version>[^<]*</version>", f"<version>{version}</version>", block)
|
||||
updated = re.sub(r"<creationDate>[^<]*</creationDate>", f"<creationDate>{date}</creationDate>", updated)
|
||||
if "<sha256>" in updated:
|
||||
updated = re.sub(r"<sha256>[^<]*</sha256>", f"<sha256>{sha256}</sha256>", updated)
|
||||
else:
|
||||
updated = updated.replace("</downloads>", f"</downloads>\n <sha256>{sha256}</sha256>")
|
||||
updated = re.sub(r"(<downloadurl[^>]*>)[^<]*(</downloadurl>)", rf"\g<1>{download_url}\g<2>", updated)
|
||||
content = content.replace(block, updated)
|
||||
print(f"Updated {xml_tag} channel: version={version}")
|
||||
else:
|
||||
print(f"WARNING: No <tag>{xml_tag}</tag> block in updates.xml")
|
||||
|
||||
with open("updates.xml", "w") as f:
|
||||
f.write(content)
|
||||
PYEOF
|
||||
|
||||
# Commit and push to current branch
|
||||
# Commit and push
|
||||
if ! git diff --quiet updates.xml 2>/dev/null; then
|
||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "gitea-actions[bot]"
|
||||
@@ -380,13 +191,11 @@ jobs:
|
||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "gitea-actions[bot]"
|
||||
|
||||
# Sync updates.xml to main and dev (whichever isn't current)
|
||||
for BRANCH in main dev; do
|
||||
[ "$BRANCH" = "$CURRENT_BRANCH" ] && continue
|
||||
|
||||
echo "Syncing updates.xml -> ${BRANCH}"
|
||||
git fetch origin "${BRANCH}" 2>/dev/null || continue
|
||||
git checkout "origin/${BRANCH}" -- . 2>/dev/null || continue
|
||||
git checkout "origin/${BRANCH}" -- updates.xml 2>/dev/null || continue
|
||||
git checkout "${CURRENT_BRANCH}" -- updates.xml
|
||||
if ! git diff --quiet updates.xml 2>/dev/null; then
|
||||
git add updates.xml
|
||||
@@ -400,29 +209,25 @@ jobs:
|
||||
continue-on-error: true
|
||||
run: |
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
TOKEN="${{ secrets.GA_TOKEN }}"
|
||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||
|
||||
php ${MOKO_CLI}/release_cascade.php \
|
||||
--stability "${{ steps.meta.outputs.stability }}" \
|
||||
--token "${TOKEN}" \
|
||||
--api-base "${API_BASE}"
|
||||
|
||||
- name: Summary
|
||||
if: always()
|
||||
run: |
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||
|
||||
# Cascade: rc -> beta,alpha,dev | beta -> alpha,dev | alpha -> dev | dev -> nothing
|
||||
case "$STABILITY" in
|
||||
release-candidate) TAGS_TO_DELETE="beta alpha development" ;;
|
||||
beta) TAGS_TO_DELETE="alpha development" ;;
|
||||
alpha) TAGS_TO_DELETE="development" ;;
|
||||
*) TAGS_TO_DELETE="" ;;
|
||||
esac
|
||||
|
||||
[ -z "$TAGS_TO_DELETE" ] && exit 0
|
||||
|
||||
for TAG in $TAGS_TO_DELETE; do
|
||||
RELEASE_ID=$(curl -sS -H "Authorization: token ${TOKEN}" \
|
||||
"${API_BASE}/releases/tags/${TAG}" 2>/dev/null | \
|
||||
python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
|
||||
|
||||
if [ -n "$RELEASE_ID" ] && [ "$RELEASE_ID" != "None" ]; then
|
||||
curl -sS -X DELETE -H "Authorization: token ${TOKEN}" \
|
||||
"${API_BASE}/releases/${RELEASE_ID}" 2>/dev/null || true
|
||||
curl -sS -X DELETE -H "Authorization: token ${TOKEN}" \
|
||||
"${API_BASE}/tags/${TAG}" 2>/dev/null || true
|
||||
echo "Deleted: ${TAG} (id: ${RELEASE_ID})"
|
||||
fi
|
||||
done
|
||||
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
||||
SHA256="${{ steps.package.outputs.sha256_zip }}"
|
||||
echo "## Pre-Release Complete" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Channel | ${STABILITY} |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Package | \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| SHA-256 | \`${SHA256:-n/a}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
@@ -49,22 +49,18 @@ env:
|
||||
SCRIPTS_ALLOWED_DIRS: scripts,scripts/fix,scripts/lib,scripts/release,scripts/run,scripts/validate
|
||||
|
||||
# Repo health policy
|
||||
REPO_REQUIRED_ARTIFACTS: README.md,LICENSE,CHANGELOG.md,.gitea/workflows/
|
||||
REPO_OPTIONAL_FILES: SECURITY.md,.editorconfig,.gitattributes,.gitignore,README.md,docs/
|
||||
REPO_REQUIRED_ARTIFACTS: README.md,LICENSE,CHANGELOG.md,CONTRIBUTING.md,CODE_OF_CONDUCT.md,.mokogitea/workflows/
|
||||
REPO_OPTIONAL_FILES: SECURITY.md,GOVERNANCE.md,.editorconfig,.gitattributes,.gitignore,README.md,docs/
|
||||
REPO_DISALLOWED_DIRS:
|
||||
REPO_DISALLOWED_FILES: TODO.md,todo.md
|
||||
|
||||
# Wiki-preferred documentation -- wiki is full credit, repo file is advisory
|
||||
# Format: filename:WikiPageName (Gitea wiki page slug)
|
||||
WIKI_PREFERRED_DOCS: CONTRIBUTING.md:Contributing,CODE_OF_CONDUCT.md:Code-of-Conduct,GOVERNANCE.md:Governance
|
||||
|
||||
# Extended checks toggles
|
||||
EXTENDED_CHECKS: "true"
|
||||
|
||||
# File / directory variables
|
||||
DOCS_INDEX: docs/docs-index.md
|
||||
SCRIPT_DIR: scripts
|
||||
WORKFLOWS_DIR: .gitea/workflows
|
||||
WORKFLOWS_DIR: .mokogitea/workflows
|
||||
SHELLCHECK_PATTERN: '*.sh'
|
||||
SPDX_FILE_GLOBS: '*.sh,*.php,*.js,*.ts,*.css,*.xml,*.yml,*.yaml'
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
@@ -85,7 +81,7 @@ jobs:
|
||||
- name: Check actor permission (admin only)
|
||||
id: perm
|
||||
env:
|
||||
TOKEN: ${{ secrets.GA_TOKEN || secrets.GA_TOKEN || github.token }}
|
||||
TOKEN: ${{ secrets.MOKOGITEA_TOKEN || secrets.MOKOGITEA_TOKEN || github.token }}
|
||||
REPO: ${{ github.repository }}
|
||||
ACTOR: ${{ github.actor }}
|
||||
run: |
|
||||
@@ -94,7 +90,7 @@ jobs:
|
||||
PERMISSION=unknown
|
||||
METHOD=""
|
||||
|
||||
# Hardcoded authorized users -- always allowed
|
||||
# Hardcoded authorized users — always allowed
|
||||
case "$ACTOR" in
|
||||
jmiller|gitea-actions[bot])
|
||||
ALLOWED=true
|
||||
@@ -369,10 +365,6 @@ jobs:
|
||||
- name: Repository health checks
|
||||
env:
|
||||
PROFILE_RAW: ${{ github.event.inputs.profile }}
|
||||
GA_TOKEN: ${{ secrets.GA_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||
GITHUB_SERVER_URL: ${{ github.server_url }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
@@ -530,63 +522,6 @@ jobs:
|
||||
} >> "${GITHUB_STEP_SUMMARY}"
|
||||
fi
|
||||
|
||||
# -- Wiki-preferred documentation checks --
|
||||
# Docs that belong in the wiki; repo-file fallback is accepted but advisory
|
||||
wiki_findings=()
|
||||
wiki_ok=()
|
||||
wiki_file_fallback=()
|
||||
wiki_missing=()
|
||||
|
||||
API_BASE="${GITHUB_SERVER_URL:-https://git.mokoconsulting.tech}"
|
||||
WIKI_TOKEN="${WIKI_TOKEN:-${GA_TOKEN:-${GITHUB_TOKEN}}}"
|
||||
REPO_FULL="${GITHUB_REPOSITORY}"
|
||||
|
||||
IFS=',' read -r -a wiki_docs <<< "${WIKI_PREFERRED_DOCS}"
|
||||
for entry in "${wiki_docs[@]}"; do
|
||||
file="${entry%%:*}"
|
||||
page="${entry##*:}"
|
||||
|
||||
# Check wiki via Gitea API
|
||||
wiki_exists=false
|
||||
http_code=$(curl -sf -o /dev/null -w '%{http_code}' \
|
||||
-H "Authorization: token ${WIKI_TOKEN}" \
|
||||
"${API_BASE}/api/v1/repos/${REPO_FULL}/wiki/page/${page}" 2>/dev/null || echo "000")
|
||||
[ "${http_code}" = "200" ] && wiki_exists=true
|
||||
|
||||
# Check repo file
|
||||
file_exists=false
|
||||
[ -f "${file}" ] && file_exists=true
|
||||
|
||||
if [ "${wiki_exists}" = true ]; then
|
||||
wiki_ok+=("${file} -> wiki/${page}")
|
||||
elif [ "${file_exists}" = true ]; then
|
||||
wiki_file_fallback+=("${file}")
|
||||
content_warnings+=("${file} found in repo root (preferred location: wiki/${page})")
|
||||
else
|
||||
wiki_missing+=("${file} (wiki/${page})")
|
||||
missing_optional+=("${file} (not in wiki or repo)")
|
||||
fi
|
||||
done
|
||||
|
||||
{
|
||||
printf '%s\n' '### Wiki-preferred documentation'
|
||||
printf '%s\n' '| Document | Location | Status |'
|
||||
printf '%s\n' '|---|---|---|'
|
||||
for item in "${wiki_ok[@]:-}"; do
|
||||
[ -z "${item}" ] && continue
|
||||
printf '%s\n' "| ${item%%->*}| Wiki | OK |"
|
||||
done
|
||||
for item in "${wiki_file_fallback[@]:-}"; do
|
||||
[ -z "${item}" ] && continue
|
||||
printf '%s\n' "| ${item} | Repo file | Advisory -- migrate to wiki |"
|
||||
done
|
||||
for item in "${wiki_missing[@]:-}"; do
|
||||
[ -z "${item}" ] && continue
|
||||
printf '%s\n' "| ${item%%(*}| Missing | Warning |"
|
||||
done
|
||||
printf '\n'
|
||||
} >> "${GITHUB_STEP_SUMMARY}"
|
||||
|
||||
# -- Joomla-specific checks --
|
||||
joomla_findings=()
|
||||
|
||||
@@ -772,13 +707,6 @@ jobs:
|
||||
printf '%s\n' '| Release variables | OK | Repository variables validation |'
|
||||
printf '%s\n' '| Scripts governance | OK | Directory policy and advisory reporting |'
|
||||
printf '%s\n' '| Repo required artifacts | OK | Required, optional, disallowed enforcement |'
|
||||
if [ "${#wiki_file_fallback[@]}" -gt 0 ]; then
|
||||
printf '%s\n' "| Wiki-preferred docs | Advisory | ${#wiki_file_fallback[@]} doc(s) in repo, prefer wiki |"
|
||||
elif [ "${#wiki_missing[@]}" -gt 0 ]; then
|
||||
printf '%s\n' "| Wiki-preferred docs | Warning | ${#wiki_missing[@]} doc(s) missing from wiki and repo |"
|
||||
else
|
||||
printf '%s\n' '| Wiki-preferred docs | OK | All docs found in wiki |'
|
||||
fi
|
||||
printf '%s\n' '| Repo content heuristics | OK | Brand, license, changelog structure |'
|
||||
if [ "${extended_enabled}" = 'true' ]; then
|
||||
if [ "${#extended_findings[@]}" -gt 0 ]; then
|
||||
|
||||
@@ -0,0 +1,312 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: moko-platform.Universal
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
# PATH: /templates/workflows/update-server.yml
|
||||
# VERSION: 05.00.00
|
||||
# BRIEF: Pre-release build + update server XML for dev/alpha/beta/rc branches
|
||||
#
|
||||
# Thin wrapper around moko-platform CLI tools.
|
||||
# Builds packages, updates updates.xml, and optionally deploys via SFTP.
|
||||
#
|
||||
# Joomla filters update entries by the user's "Minimum Stability" setting.
|
||||
|
||||
name: "Update Server"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'dev'
|
||||
- 'dev/**'
|
||||
- 'alpha/**'
|
||||
- 'beta/**'
|
||||
- 'rc/**'
|
||||
paths:
|
||||
- 'src/**'
|
||||
- 'htdocs/**'
|
||||
pull_request:
|
||||
types: [closed]
|
||||
branches:
|
||||
- 'dev'
|
||||
- 'dev/**'
|
||||
- 'alpha/**'
|
||||
- 'beta/**'
|
||||
- 'rc/**'
|
||||
paths:
|
||||
- 'src/**'
|
||||
- 'htdocs/**'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
stability:
|
||||
description: 'Stability tag'
|
||||
required: true
|
||||
default: 'development'
|
||||
type: choice
|
||||
options:
|
||||
- development
|
||||
- alpha
|
||||
- beta
|
||||
- rc
|
||||
- stable
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
|
||||
GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
update-xml:
|
||||
name: Update Server
|
||||
runs-on: release
|
||||
if: >-
|
||||
github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' || github.event_name == 'push'
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup moko-platform tools
|
||||
env:
|
||||
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
||||
COMPOSER_AUTH: '{"http-basic":{"git.mokoconsulting.tech":{"username":"token","password":"${{ secrets.MOKOGITEA_TOKEN }}"}}}'
|
||||
run: |
|
||||
if ! command -v composer &> /dev/null; then
|
||||
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1
|
||||
fi
|
||||
# Always fetch latest CLI tools — never use stale cache from previous runs
|
||||
rm -rf /tmp/moko-platform
|
||||
git clone --depth 1 --branch main --quiet \
|
||||
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
|
||||
/tmp/moko-platform 2>/dev/null || true
|
||||
if [ -d "/tmp/moko-platform" ] && [ -f "/tmp/moko-platform/composer.json" ]; then
|
||||
cd /tmp/moko-platform && composer install --no-dev --no-interaction --quiet 2>/dev/null || true
|
||||
fi
|
||||
echo "MOKO_CLI=/tmp/moko-platform/cli" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Detect platform
|
||||
id: platform
|
||||
run: php ${MOKO_CLI}/manifest_read.php --path . --github-output
|
||||
|
||||
- name: Resolve stability and bump version
|
||||
id: meta
|
||||
run: |
|
||||
BRANCH="${{ github.ref_name }}"
|
||||
|
||||
# Configure git for bot pushes
|
||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "gitea-actions[bot]"
|
||||
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||
|
||||
# Auto-bump patch version
|
||||
php ${MOKO_CLI}/version_bump.php --path . 2>/dev/null || true
|
||||
|
||||
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "0.0.0")
|
||||
|
||||
# Strip any existing suffix before applying stability
|
||||
VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//')
|
||||
|
||||
# Determine stability from branch or manual input
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
STABILITY="${{ inputs.stability }}"
|
||||
elif [[ "$BRANCH" == rc/* ]]; then
|
||||
STABILITY="rc"
|
||||
elif [[ "$BRANCH" == beta/* ]]; then
|
||||
STABILITY="beta"
|
||||
elif [[ "$BRANCH" == alpha/* ]]; then
|
||||
STABILITY="alpha"
|
||||
else
|
||||
STABILITY="development"
|
||||
fi
|
||||
|
||||
# Version suffix per stability stream
|
||||
case "$STABILITY" in
|
||||
development) SUFFIX="-dev"; TAG="development" ;;
|
||||
alpha) SUFFIX="-alpha"; TAG="alpha" ;;
|
||||
beta) SUFFIX="-beta"; TAG="beta" ;;
|
||||
rc) SUFFIX="-rc"; TAG="release-candidate" ;;
|
||||
*) SUFFIX=""; TAG="stable" ;;
|
||||
esac
|
||||
|
||||
# Propagate version with stability suffix to all manifest files
|
||||
php ${MOKO_CLI}/version_set_platform.php \
|
||||
--path . --version "$VERSION" --branch "$BRANCH" --stability "$STABILITY" 2>/dev/null || true
|
||||
php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
|
||||
|
||||
# Re-read version (now includes suffix from version_set_platform)
|
||||
if [ -n "$SUFFIX" ]; then
|
||||
VERSION="${VERSION}${SUFFIX}"
|
||||
fi
|
||||
|
||||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT"
|
||||
echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT"
|
||||
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
|
||||
echo "display_version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# Commit version bump if changed
|
||||
git add -A
|
||||
git diff --cached --quiet || {
|
||||
git commit -m "chore(version): auto-bump ${VERSION} [skip ci]" \
|
||||
--author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>"
|
||||
git push
|
||||
}
|
||||
|
||||
- name: Create release and upload package
|
||||
id: package
|
||||
run: |
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
TAG="${{ steps.meta.outputs.tag }}"
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
|
||||
# Create or update Gitea release
|
||||
php ${MOKO_CLI}/release_create.php \
|
||||
--path . --version "$VERSION" --tag "$TAG" \
|
||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
||||
--repo "${GITEA_REPO}" --branch "${{ github.ref_name }}" --prerelease
|
||||
|
||||
# Build package and upload
|
||||
php ${MOKO_CLI}/release_package.php \
|
||||
--path . --version "$VERSION" --tag "$TAG" \
|
||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
||||
--repo "${GITEA_REPO}" --output /tmp || true
|
||||
|
||||
- name: Update updates.xml
|
||||
if: steps.platform.outputs.platform == 'joomla'
|
||||
run: |
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||
SHA256="${{ steps.package.outputs.sha256_zip }}"
|
||||
|
||||
if [ ! -f "updates.xml" ]; then
|
||||
echo "No updates.xml — skipping"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
SHA_FLAG=""
|
||||
[ -n "$SHA256" ] && SHA_FLAG="--sha ${SHA256}"
|
||||
|
||||
php ${MOKO_CLI}/updates_xml_build.php \
|
||||
--path . --version "${VERSION}" --stability "${STABILITY}" \
|
||||
--gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \
|
||||
${SHA_FLAG}
|
||||
|
||||
# Commit and push updates.xml
|
||||
git add updates.xml
|
||||
git diff --cached --quiet || {
|
||||
git commit -m "chore: update ${STABILITY} channel ${VERSION} [skip ci]"
|
||||
git push
|
||||
}
|
||||
|
||||
- name: Sync updates.xml to main
|
||||
if: github.ref_name != 'main' && steps.platform.outputs.platform == 'joomla'
|
||||
run: |
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
GITEA_TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||
|
||||
FILE_SHA=$(curl -sf -H "Authorization: token ${GITEA_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
|
||||
python3 -c "
|
||||
import base64, json, urllib.request, sys
|
||||
with open('updates.xml', 'rb') as f:
|
||||
content = base64.b64encode(f.read()).decode()
|
||||
payload = json.dumps({
|
||||
'content': content,
|
||||
'sha': '${FILE_SHA}',
|
||||
'message': 'chore: sync updates.xml from ${{ steps.meta.outputs.stability }} [skip ci]',
|
||||
'branch': 'main'
|
||||
}).encode()
|
||||
req = urllib.request.Request(
|
||||
'${API_BASE}/contents/updates.xml',
|
||||
data=payload, method='PUT',
|
||||
headers={
|
||||
'Authorization': 'token ${GITEA_TOKEN}',
|
||||
'Content-Type': 'application/json'
|
||||
})
|
||||
try:
|
||||
urllib.request.urlopen(req)
|
||||
print('updates.xml synced to main')
|
||||
except Exception as e:
|
||||
print(f'WARNING: sync to main failed: {e}', file=sys.stderr)
|
||||
"
|
||||
fi
|
||||
|
||||
- name: SFTP deploy to dev server
|
||||
if: contains(github.ref, 'dev/') || github.ref == 'refs/heads/dev'
|
||||
env:
|
||||
DEV_HOST: ${{ vars.DEV_FTP_HOST }}
|
||||
DEV_PATH: ${{ vars.DEV_FTP_PATH }}
|
||||
DEV_SUFFIX: ${{ vars.DEV_FTP_SUFFIX }}
|
||||
DEV_USER: ${{ vars.DEV_FTP_USERNAME }}
|
||||
DEV_PORT: ${{ vars.DEV_FTP_PORT }}
|
||||
DEV_KEY: ${{ secrets.DEV_FTP_KEY }}
|
||||
DEV_PASS: ${{ secrets.DEV_FTP_PASSWORD }}
|
||||
run: |
|
||||
# Permission check: admin or maintain role required
|
||||
ACTOR="${{ github.actor }}"
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
|
||||
PERMISSION=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||
"${API_BASE}/collaborators/${ACTOR}/permission" 2>/dev/null | \
|
||||
python3 -c "import sys,json; print(json.load(sys.stdin).get('permission','read'))" 2>/dev/null || echo "read")
|
||||
case "$PERMISSION" in
|
||||
admin|maintain|write) ;;
|
||||
*)
|
||||
echo "Deploy denied: ${ACTOR} has '${PERMISSION}' — requires admin, maintain, or write"
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
[ -z "$DEV_HOST" ] || [ -z "$DEV_PATH" ] && { echo "DEV FTP not configured — skipping SFTP"; exit 0; }
|
||||
|
||||
SOURCE_DIR="src"
|
||||
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
|
||||
[ ! -d "$SOURCE_DIR" ] && exit 0
|
||||
|
||||
PORT="${DEV_PORT:-22}"
|
||||
REMOTE="${DEV_PATH%/}"
|
||||
[ -n "$DEV_SUFFIX" ] && REMOTE="${REMOTE}/${DEV_SUFFIX#/}"
|
||||
|
||||
printf '{"host":"%s","port":%s,"username":"%s","remotePath":"%s"' \
|
||||
"$DEV_HOST" "$PORT" "$DEV_USER" "$REMOTE" > /tmp/sftp-config.json
|
||||
if [ -n "$DEV_KEY" ]; then
|
||||
echo "$DEV_KEY" > /tmp/deploy_key && chmod 600 /tmp/deploy_key
|
||||
printf ',"privateKeyPath":"/tmp/deploy_key"}' >> /tmp/sftp-config.json
|
||||
else
|
||||
printf ',"password":"%s"}' "$DEV_PASS" >> /tmp/sftp-config.json
|
||||
fi
|
||||
|
||||
PLATFORM=$(php ${MOKO_CLI}/platform_detect.php --path . 2>/dev/null || true)
|
||||
if [ "$PLATFORM" = "waas-component" ] && [ -f "${MOKO_CLI}/../deploy/deploy-joomla.php" ]; then
|
||||
php ${MOKO_CLI}/../deploy/deploy-joomla.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json
|
||||
elif [ -f "${MOKO_CLI}/../deploy/deploy-sftp.php" ]; then
|
||||
php ${MOKO_CLI}/../deploy/deploy-sftp.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json
|
||||
fi
|
||||
rm -f /tmp/deploy_key /tmp/sftp-config.json
|
||||
echo "SFTP deploy to dev complete" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Summary
|
||||
if: always()
|
||||
run: |
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||
DISPLAY="${{ steps.meta.outputs.display_version }}"
|
||||
echo "## Update Server" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Stability | \`${STABILITY}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Version | \`${DISPLAY}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
+33
-662
@@ -13,676 +13,47 @@
|
||||
-->
|
||||
|
||||
# Changelog — MokoOnyx (VERSION: 03.09.03)
|
||||
## [Unreleased]
|
||||
|
||||
## [02.08.00] --- 2026-05-29
|
||||
|
||||
## [02.08.00] --- 2026-05-29
|
||||
|
||||
## [02.08.00] --- 2026-05-29
|
||||
|
||||
## [02.08.00] --- 2026-05-28
|
||||
|
||||
|
||||
All notable changes to the MokoOnyx Joomla template are documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
## [02.08.01] --- 2026-05-28
|
||||
|
||||
### Changed
|
||||
- Migrated all workflow and template paths from `.github/` to `.mokogitea/`
|
||||
- Template source paths updated: `templates/gitea/` to `templates/mokogitea/`
|
||||
- HCL definition files removed -- Template repos are now the canonical source
|
||||
|
||||
### Added
|
||||
- `branch-cleanup.yml`: auto-delete merged feature branches after PR merge
|
||||
|
||||
### Removed
|
||||
- Removed deploy-manual.yml workflow — switching to Joomla update server method for extension distribution
|
||||
- Removed deploy-manual.yml workflow -- switching to Joomla update server method for extension distribution
|
||||
- Removed deploy variables and secrets (DEV_FTP_*)
|
||||
- **Theme preview removed** -- Removed unused language strings for theme preview feature (never wired into config)
|
||||
- **Repo cleanup** -- Removed 38 unused files: Fredoka/Pacifico fonts, dead npm tooling (package.json, scripts/), tmp-overrides/, placeholder tests, orphaned workflow copies, stale READMEs
|
||||
- **Broken font options removed** -- Removed Noto Sans and Fira Sans from font selector (CSS files never existed)
|
||||
- **Docs moved to wiki** -- CONTRIBUTING.md, CODE_OF_CONDUCT.md, GOVERNANCE.md migrated to Gitea wiki
|
||||
|
||||
### Changed
|
||||
- **repo-health.yml** -- Wiki-preferred documentation checks via Gitea API (wiki = full credit, repo file = advisory)
|
||||
- **auto-release.yml Step 8b** -- Replaced inline Python with release_body_update.php CLI tool (fixes SIGPIPE exit 141)
|
||||
- **pre-release.yml rewritten** -- Uses moko-platform CLI tools, PHP instead of Python, fixed broken platform detection
|
||||
- **All workflow YAML files** -- Stripped non-ASCII characters (em dashes, arrows, emoji) for Gitea YAML parser compatibility
|
||||
|
||||
### Fixed
|
||||
- **Menu icon padding removed** — Removed hardcoded `p-2` class from all mod_menu icon spans (horizontal + mainmenu layouts); icons now inherit spacing from the parent link
|
||||
|
||||
## [02.06.00] - 2026-05-16
|
||||
|
||||
### Added
|
||||
- **Community Builder plugin type** — Auto-release detects `<cbplugin>` manifests and uses `cb_` prefix
|
||||
- **Platform auto-sense** — Workflow detects Joomla/Dolibarr/generic from file structure without manifest
|
||||
- **Font Awesome + Bootstrap in component view** — Print/modal view now loads FA7 and Bootstrap 5
|
||||
|
||||
### Changed
|
||||
- **Release workflow dispatch-only removed** — Auto-release triggers on PR merge to main (src/htdocs paths) + manual dispatch
|
||||
- **Version bump protocol** — Releases use version as-is from main; dev bumped to next minor after release (Step 11)
|
||||
- **GitHub mirror removed** — Gitea is the sole release platform
|
||||
- **update-server.yml removed** — Merged into auto-release (stable) and pre-release (dev/rc) workflows
|
||||
- **`.mokogitea/manifest.xml` precedence** — Platform detection reads .mokogitea first, falls back to .gitea
|
||||
|
||||
### Fixed
|
||||
- **CSS variable fallbacks** — 341 `var()` calls now have explicit light theme fallback values
|
||||
- **Dropdown menu background** — Uses `--nav-bg-color` instead of `--body-bg`
|
||||
|
||||
## [02.02.00] - 2026-05-16
|
||||
|
||||
### Fixed
|
||||
- **template.css fallback values** — All var() fallbacks aligned with light.standard.css values
|
||||
- **background-color mismatches** — Fixed 20+ properties incorrectly using `--body-color` as background
|
||||
|
||||
## [02.01.06] - 2026-05-16
|
||||
|
||||
### Added
|
||||
- **Article metadata footer** — Renders Joomla custom fields (`jcfields`) as a styled metadata footer on article layouts, grouped by field group with responsive grid and BEM styling
|
||||
- **Smart Visitor Detection** — Pushes anonymised visitor properties (login status, user group, page type) to the dataLayer for Google Analytics / Tag Manager. Sets GA4 `user_properties` for persistent session-scoped dimensions. No PII is sent. Default enabled when GTM or GA4 is active.
|
||||
- **Auto-cascade workflow** — Forward-merges `main` → `dev` after every push; auto-creates a PR on conflict
|
||||
- **Component/print-view stylesheet** — Dedicated `component.css` replaces `template.css` in the component view with print-optimised styles using theme variables
|
||||
- **Print-view GA4 tracking** — Component view sends `content_group=print_view` to Google Analytics for tracking print/modal usage
|
||||
- **Custom light theme in component view** — Component view now loads `light.custom.css` when configured
|
||||
- **Auto-minification** — `user.css`, `user.js`, and custom theme files are automatically minified on page load via `MokoMinifyHelper`
|
||||
- **Media folder cleanup on install/update** — `script.php` now removes stale `.min` files, deprecated assets, and unminified vendor files during install or update
|
||||
- **Changelog auto-bump in auto-release** — `[Unreleased]` is automatically promoted to the release version on stable release, with a fresh `[Unreleased]` section inserted above
|
||||
- **CI: PHPStan static analysis** — Added to CI pipeline
|
||||
- **CI: Gitleaks secret scanning** — Added to CI pipeline
|
||||
- **CI: CSS sync workflow** — Syncs CSS to template repo and checks client variable coverage
|
||||
|
||||
### Changed
|
||||
- **Custom head params replaced with user files** — Removed `custom_head_start` / `custom_head_end` template params in favour of `user.css` and `user.js` (loaded via Web Asset Manager)
|
||||
- **User override files added to .gitignore** — `user.css` and `user.js` are client-repo only; not committed to the template repo
|
||||
- **Asset registry simplified** — Removed duplicate `.min` entries from `joomla.asset.json`; Joomla's Web Asset Manager auto-resolves `.min` variants when debug is off
|
||||
- **Font Awesome vendor ships minified only** — Removed unminified FA7 Free CSS (saves ~21 kLOC); asset entries now point to `.min.css` directly
|
||||
- **Workflows restructured — CI/CD workflows and issue templates reorganized under `.gitea/` with template sync
|
||||
|
||||
### Removed
|
||||
- **Migration tab** — Removed MokoCassiopeia migration fieldset and associated language strings from template params
|
||||
- **Migration description** — Removed migration callout and "formerly MokoCassiopeia" reference from template description
|
||||
- **Custom head fields** — Removed `custom_head_start` / `custom_head_end` fields and `Custom Code` fieldset from template configuration
|
||||
- **Footer from component view** — Removed footer module positions from `component.php` (print/modal view)
|
||||
- **Unminified vendor CSS** — Removed `all.css`, `brands.css`, `fontawesome.css`, `regular.css`, `solid.css` from FA7 Free (21k+ lines)
|
||||
|
||||
## [03.10.00] - 2026-04-18
|
||||
|
||||
### Important
|
||||
- **Template Consolidation** — This release finalised the MokoOnyx identity, adding automatic migration from legacy MokoCassiopeia installations. Settings, menu assignments, and files are imported on first page load.
|
||||
|
||||
### Added
|
||||
- **Offline page redesign** — Full-viewport background from Joomla offline_image or header background, glass card overlay, centered logo with glow, login accordion, copyright footer
|
||||
- **CSS variable click-to-copy** — Text containing `--variable-name` patterns is wrapped in clickable chips that copy to clipboard with toast notification
|
||||
- **Brand-aside 3-column layout** — Flex columns like top-a with card style
|
||||
- **mod_stats table layout** — Converted from definition list to semantic table
|
||||
- **Favicon multi-format support** — Now handles PNG, JPEG, GIF, WebP, BMP (not just PNG)
|
||||
- **Theme variables** — `--theme-fab-bg`, `--theme-fab-color`, `--theme-fab-btn-bg`, `--theme-fab-border`, `--offline-card-bg`
|
||||
- **Footer CSS variables** — Added to CSS Variables reference tab
|
||||
- **Bridge migration script** — `helper/bridge.php` handles automatic MokoCassiopeia → MokoOnyx migration
|
||||
- **Dedicated release runner** — Release workflows run on isolated `release` label runner
|
||||
- **Runner fleet** — 3 CI + 1 release runner (12 concurrent jobs)
|
||||
|
||||
### Changed
|
||||
- **Gitea-primary CI/CD** — All workflows use Gitea API, GitHub is backup for stable/RC only
|
||||
- **Theme switcher** — Larger, bordered, theme-aware colors (off-white on dark, primary on light)
|
||||
- **Auto switch** — Red when off, green when on
|
||||
- **A11y toolbar** — Theme-aware colors for dark mode visibility
|
||||
- **Search button border** — Matches input border (`--input-border-color`)
|
||||
- **Offline message** — 0=hidden, 1=custom message, 2=system language string
|
||||
- **Light theme fonts** — Fixed trailing `)` syntax error, normalized quote style to match dark
|
||||
- **`--accent-color-secondary`** — Unified to `#6fb3ff` across both themes
|
||||
- **`--alert-color`** — Set to `#000` in light theme
|
||||
|
||||
### Removed
|
||||
- Brand showcase tab (redundant with theme preview)
|
||||
- Position selectors for a11y/theme FAB (forced to bottom-right)
|
||||
- Custom theme CSS from git tracking (site-specific, gitignored)
|
||||
|
||||
### Fixed
|
||||
- SHA-256 checksum format — Removed `sha256:` prefix (Joomla expects raw hex)
|
||||
- Favicon path resolution — Strips `#joomlaImage://` fragment, tries multiple path candidates
|
||||
- `REQUIRE_SIGNIN_VIEW` — Set to `false` for public release downloads
|
||||
- Release workflow — Uses Gitea API to update `updates.xml` on main (bypasses branch protection)
|
||||
- Language loading on offline page — `com_users` and core language files loaded explicitly
|
||||
|
||||
---
|
||||
|
||||
## [03.09.03] - 2026-04-02
|
||||
|
||||
### Added
|
||||
- **Favicon configuration** — New "Favicon" tab in template config; upload a PNG and all favicon sizes are auto-generated via PHP GD (ICO, Apple Touch Icon 180px, Android Chrome 192/512px, site.webmanifest)
|
||||
- **Module overrides** — 11 new `default.php` layout overrides for Joomla core modules: `mod_custom`, `mod_articles_latest`, `mod_articles_popular`, `mod_articles_news`, `mod_articles_category`, `mod_breadcrumbs`, `mod_footer`, `mod_login`, `mod_finder`, `mod_tags_popular`, `mod_tags_similar`, `mod_related_items`
|
||||
- **Module title support** — All module overrides respect `$module->showtitle`, `header_tag`, `header_class`, and `moduleclass_sfx` parameters
|
||||
- **Module CSS** — BEM-scoped styles for module titles, article lists, tag badges, search forms, login forms, breadcrumbs, and footer content
|
||||
- **Hero card variables** — Full variable-driven hero system: `--hero-card-bg`, `--hero-card-color`, `--hero-card-overlay`, `--hero-card-border-radius`, `--hero-card-padding-x/y`, `--hero-card-max-width`, plus `--hero-alt-card-*` for secondary variant
|
||||
- **Hero mobile breakpoint** — Photo background hidden on mobile (≤767.98px), hero card becomes full-bleed (100dvh, no border-radius)
|
||||
- **CSS fallback values** — 1365 `var()` calls in template.css now include inline fallback values
|
||||
- **Card border-radius** — `.card` now has `.25rem` fallback on `var(--card-border-radius)`
|
||||
|
||||
### Changed
|
||||
- **Button backgrounds** — `--btn-bg: transparent` changed to `var(--body-bg)` in dark and light themes
|
||||
- **Offcanvas close button** — `.offcanvas-header .btn-close` now gets `background-color` from `--offcanvas-bg`
|
||||
- **Custom template sync** — Both `dark.custom.css` and `light.custom.css` now contain all variables from their standard counterparts (was missing 223 variables)
|
||||
- **Overlay layer** — Added `--hero-overlay-bg-position` and `--hero-overlay-bg-size` variables
|
||||
- **Legacy CSS cleanup** — Removed vendor prefixes (`-webkit-box`, `-ms-flexbox`) from `.overlay` rules, replaced with modern flexbox
|
||||
|
||||
### Removed
|
||||
- **FILE INFORMATION headers** — Stripped DEFGROUP/INGROUP/PATH/VERSION/BRIEF metadata from all PHP, CSS, JS, INI, and HTML files (kept in XML and README per policy)
|
||||
- **Mobile overrides** — Deleted 26 `mobile.php` layout files and their empty parent directories
|
||||
- **Joomla-specific gitignore entries** — Removed ~700 lines of Joomla CMS core paths from `.gitignore` (not applicable to a template repository)
|
||||
|
||||
### Fixed
|
||||
- **CI: composer install** — Workflow `standards-compliance.yml` now conditionally runs `composer install` only when `composer.json` exists
|
||||
- **CI: YAML syntax** — Fixed invalid YAML in `auto-update-sha.yml` caused by multiline commit message in run block
|
||||
|
||||
---
|
||||
|
||||
## [03.09.02] - 2026-03-26
|
||||
|
||||
### Added - Hero Variant System & Block Color System
|
||||
|
||||
#### Hero Variants
|
||||
- **`.hero#primary`** and **`.hero#secondary`** CSS variant system for visually distinct hero treatments
|
||||
- Shared `.hero` base class with `background-size: cover`, `border-radius: .5rem`, and `overflow: hidden`
|
||||
- Six new CSS variables (`--hero-primary-bg-color`, `--hero-primary-overlay`, `--hero-primary-color`, and secondary equivalents)
|
||||
- Light and dark mode defaults in custom palette templates
|
||||
|
||||
#### Block Color System
|
||||
- Automatic `:nth-child()` slot palette for `top-a`, `top-b`, `bottom-a`, `bottom-b` module positions
|
||||
- Four color slots (`--block-color-1` through `--block-color-4`) with matching text variables
|
||||
- Named per-module overrides: `#block-highlight`, `#block-cta`, `#block-alert`
|
||||
- ID specificity wins over `:nth-child()` — no `!important` needed
|
||||
|
||||
#### Files Modified
|
||||
- `src/media/css/template.css` — hero variant rules, block color `:nth-child()` rules, named override rules
|
||||
- `src/media/css/theme/light.standard.css` — hero and block color variables (light standard)
|
||||
- `src/media/css/theme/dark.standard.css` — hero and block color variables (dark standard)
|
||||
- `src/templates/light.custom.css` — hero and block color variables (light custom starter)
|
||||
- `src/templates/dark.custom.css` — hero and block color variables (dark custom starter)
|
||||
- `src/templateDetails.xml` — Theme Preview tab, hero/block note fields, scriptfile registration, version bump to 03.09.02
|
||||
- `src/language/en-GB/tpl_mokoonyx.ini` — language strings for new admin fields (British English)
|
||||
- `src/language/en-US/tpl_mokoonyx.ini` — language strings for new admin fields (American English)
|
||||
- `docs/CSS_VARIABLES.md` — full variable reference for both systems, sync script documentation
|
||||
- `CHANGELOG.md` — this entry
|
||||
|
||||
#### Files Added
|
||||
- `src/templates/theme-test.html` — Bootstrap-style test page with branded showcase, CSS variable swatches, hero demos, block color demos, and color test image
|
||||
- `src/script.php` — Joomla install/update lifecycle script (runs CSS variable sync on upgrade, checks PHP/Joomla minimum versions)
|
||||
- `src/sync_custom_vars.php` — CLI/library utility that detects missing CSS variables in user custom palettes and injects them
|
||||
- `src/templates/brand-showcase.html` — Interactive color system gradients with hover pixel sampler, Bootstrap component showcase
|
||||
|
||||
#### Variable Audit
|
||||
- All 20 hero/block variables confirmed present in all 4 theme files (light/dark standard + custom)
|
||||
- No duplicate variable declarations found across any theme file
|
||||
- `--gutter-x` references in template.css are self-scoped to grid containers (standard Bootstrap 5 behavior, not a `:root` variable)
|
||||
|
||||
---
|
||||
|
||||
## [03.08.03] - 2026-02-27
|
||||
|
||||
### Added - Main Menu Collapsible Dropdown Override
|
||||
|
||||
**New feature**: Added responsive "Main Menu" mod_menu override with Bootstrap 5 collapsible dropdown functionality.
|
||||
|
||||
#### What's New
|
||||
- **Main Menu module override** with full Bootstrap 5 responsive navbar
|
||||
- Collapsible hamburger menu for mobile devices
|
||||
- Multi-level dropdown support with hover on desktop, tap on mobile
|
||||
- WCAG 2.1 compliant touch targets (48px on mobile, 44px on desktop)
|
||||
- BEM naming convention: `.mod-menu-main__*`
|
||||
- **Appears as "Mainmenu" layout option** in Joomla admin module settings
|
||||
|
||||
#### Files Added
|
||||
- `src/templates/html/mod_menu/mainmenu.php` - Main layout with Bootstrap navbar
|
||||
- `src/templates/html/mod_menu/mainmenu_component.php` - Component menu items
|
||||
- `src/templates/html/mod_menu/mainmenu_heading.php` - Heading menu items
|
||||
- `src/templates/html/mod_menu/mainmenu_separator.php` - Separator menu items
|
||||
- `src/templates/html/mod_menu/mainmenu_url.php` - URL menu items
|
||||
- `src/templates/html/mod_menu/index.html` - Security file
|
||||
|
||||
#### Features
|
||||
- **Bootstrap 5 Navbar**: Uses Bootstrap's native navbar-nav structure
|
||||
- **Collapsible on Mobile**: Hamburger menu with smooth collapse animation
|
||||
- **Dropdown Menus**: Multi-level dropdown support with caret indicators
|
||||
- **Responsive Breakpoints**: Mobile-first design adapting at 768px and 992px
|
||||
- **Touch-Friendly**: 48px minimum touch targets on mobile
|
||||
- **Accessible**: ARIA labels and keyboard navigation support
|
||||
- **Active States**: Visual indicators for current and active menu items
|
||||
- **Alternative Layout**: Named `mainmenu.php` (not `default.php`) to appear as selectable layout option in Joomla admin
|
||||
|
||||
#### CSS Architecture
|
||||
- 200+ lines of responsive CSS in template.css
|
||||
- BEM naming: `.mod-menu-main`, `.mod-menu-main__list`, `.mod-menu-main__link`
|
||||
- CSS variables integration for colors and borders
|
||||
- Hover effects on desktop, tap effects on mobile
|
||||
- Smooth transitions and animations
|
||||
|
||||
#### Module Count Update
|
||||
- **Before**: 16 module overrides
|
||||
- **After**: 17 module overrides (added mod_menu "Main Menu")
|
||||
- **Component overrides**: Still 7 (unchanged)
|
||||
|
||||
### Removed - mod_search Override
|
||||
|
||||
**Cassiopeia approach**: Removed mod_search override to align with Cassiopeia template philosophy of not overriding standard Joomla modules.
|
||||
|
||||
#### Reason for Removal
|
||||
- mod_search is a standard Joomla core module
|
||||
- Following Cassiopeia template approach: use core layouts for standard modules
|
||||
- Prevents potential language loading issues
|
||||
- Ensures compatibility with future Joomla updates
|
||||
- Core mod_search already includes responsive design and accessibility features
|
||||
|
||||
#### Files Removed
|
||||
- `src/templates/html/mod_search/default.php` - Custom search module layout
|
||||
- `src/templates/html/mod_search/index.html` - Security file
|
||||
|
||||
#### Module Count Update (After Removal)
|
||||
- **Before**: 17 module overrides
|
||||
- **After**: 16 module overrides (removed mod_search)
|
||||
- **Component overrides**: Still 7 (unchanged)
|
||||
|
||||
### Removed - Documentation Cleanup
|
||||
|
||||
**Documentation policy**: Removed all markdown files from `src/templates/html/` directory. All documentation belongs in `docs/` folder only.
|
||||
|
||||
#### Files Removed (9 markdown files)
|
||||
- `src/templates/html/STANDARD_MODULES_README.md`
|
||||
- `src/templates/html/INDUSTRY_MODULES_README.md`
|
||||
- `src/templates/html/VIRTUEMART_MODULES_README.md`
|
||||
- `src/templates/html/mod_virtuemart_cart/README.md`
|
||||
- `src/templates/html/mod_virtuemart_category/README.md`
|
||||
- `src/templates/html/mod_virtuemart_currencies/README.md`
|
||||
- `src/templates/html/mod_virtuemart_manufacturer/README.md`
|
||||
- `src/templates/html/mod_virtuemart_product/README.md`
|
||||
- `src/templates/html/mod_search/README.md`
|
||||
|
||||
**Note**: All module override documentation is consolidated in `docs/MODULE_OVERRIDES.md`. The `src/templates/html/` directory now contains only PHP override files and `index.html` security files.
|
||||
|
||||
**Note**: Unlike the previously removed mod_menu override (v03.08.01), this new "Main Menu" override is properly structured based on Joomla core layouts and Bootstrap 5, ensuring language strings load correctly and menu functionality works as expected. The layout is named `mainmenu.php` (not `default.php`) to appear as an alternative layout option "Mainmenu" in the Joomla admin module dropdown selector, preserving Joomla's core default menu layout.
|
||||
|
||||
## [03.08.02] - 2026-02-27
|
||||
|
||||
### Removed - Fix Language Loading in All Module Overrides
|
||||
|
||||
**Critical fix**: Removed standard Joomla module overrides to fix language string loading issues. Following Cassiopeia template approach.
|
||||
|
||||
#### Problem
|
||||
- Default language strings not loading in module overrides (mod_breadcrumbs, mod_login, mod_articles_latest)
|
||||
- Language constants displayed instead of translated text (e.g., "MOD_LOGIN_VALUE_USERNAME" instead of "Username")
|
||||
- Custom overrides interfered with Joomla's module initialization and language loading process
|
||||
|
||||
#### Solution - Cassiopeia Approach
|
||||
- **Removed** standard Joomla module overrides:
|
||||
- `src/templates/html/mod_breadcrumbs/` (2 files)
|
||||
- `src/templates/html/mod_login/` (2 files)
|
||||
- `src/templates/html/mod_articles_latest/` (2 files)
|
||||
- Template now uses Joomla's core module layouts for standard modules
|
||||
- Language files load automatically via Joomla's module system
|
||||
- Custom styling can still be applied via CSS using module-specific classes
|
||||
- **Retained** third-party extension overrides where they add mobile-responsive value:
|
||||
- VirtueMart modules (5): mod_virtuemart_cart, _category, _currencies, _manufacturer, _product
|
||||
- Community Builder modules (2): mod_cblogin, mod_comprofilerOnline
|
||||
- Other extensions (9): mod_acymailing, mod_hikashop_cart, mod_k2_content, mod_kunena*, mod_osmembership, mod_search
|
||||
|
||||
#### Cassiopeia Template Philosophy
|
||||
- Cassiopeia (Joomla's default template) does NOT override standard module layouts
|
||||
- It relies on core Joomla module files and applies styling via CSS
|
||||
- Overrides are only created when structural changes are absolutely necessary
|
||||
- This ensures compatibility, automatic language loading, and easier maintenance
|
||||
|
||||
#### Module Count Update
|
||||
- **Before**: 19 module overrides
|
||||
- **After**: 16 module overrides
|
||||
- **Removed**: 3 standard Joomla modules (breadcrumbs, login, articles_latest)
|
||||
- **Component overrides**: Still 7 (unchanged)
|
||||
|
||||
#### Files Removed
|
||||
- `src/templates/html/mod_breadcrumbs/default.php`
|
||||
- `src/templates/html/mod_breadcrumbs/index.html`
|
||||
- `src/templates/html/mod_login/default.php`
|
||||
- `src/templates/html/mod_login/index.html`
|
||||
- `src/templates/html/mod_articles_latest/default.php`
|
||||
- `src/templates/html/mod_articles_latest/index.html`
|
||||
|
||||
**Note**: This follows Joomla best practices by using core layouts for standard modules. Styling is handled via CSS. Third-party extension overrides remain for mobile responsiveness.
|
||||
|
||||
## [03.08.01] - 2026-02-27
|
||||
|
||||
### Removed - Fix Breaking Overrides
|
||||
|
||||
**Critical fix**: Removed mod_menu override that was causing menu links to break and language strings not to load.
|
||||
|
||||
#### Problem
|
||||
- mod_menu override files (default.php, default_component.php, default_url.php) were attempting to load menu-specific layouts that don't exist in the template
|
||||
- This broke Joomla's core menu rendering system
|
||||
- Menu links were not functional
|
||||
- Language strings were not loading properly in menus
|
||||
|
||||
#### Solution
|
||||
- **Removed** entire `src/templates/html/mod_menu/` directory (4 files)
|
||||
- Template now uses Joomla's default menu rendering
|
||||
- Custom styling can still be applied via CSS using `.mod-menu` class
|
||||
- All menu functionality restored to standard Joomla behavior
|
||||
|
||||
#### Documentation Updates
|
||||
- Updated MODULE_OVERRIDES.md: Changed count from 20 to 19 module overrides, removed mod_menu section, added note about removal
|
||||
- Updated STANDARD_MODULES_README.md: Removed mod_menu documentation, renumbered remaining modules, updated file structure
|
||||
- Updated testing checklists to remove mod_menu references
|
||||
- **Added clarification**: MokoOnyx is a standalone template extension (not a package)
|
||||
- Updated updates.xml to version 03.08.01
|
||||
|
||||
#### Files Removed
|
||||
- `src/templates/html/mod_menu/default.php`
|
||||
- `src/templates/html/mod_menu/default_component.php`
|
||||
- `src/templates/html/mod_menu/default_url.php`
|
||||
- `src/templates/html/mod_menu/index.html`
|
||||
|
||||
**Note**: This is a patch release that removes problematic overrides to restore core functionality. Menu styling via CSS remains intact. MokoOnyx remains a standalone Joomla template extension (type="template"), not bundled as a package.
|
||||
|
||||
## [03.08.00] - 2026-02-22
|
||||
|
||||
### Added - Community Builder Component Overrides
|
||||
|
||||
Minor version bump adding **4 Community Builder component view overrides** to complement the existing CB module overrides (mod_cblogin, mod_comprofilerOnline).
|
||||
|
||||
#### Community Builder Components (4 views)
|
||||
- **com_comprofiler/userprofile**: User profile display with avatar, tabs, and custom fields in responsive layout
|
||||
- **com_comprofiler/userslist**: User directory with search functionality and responsive grid (1-3 columns)
|
||||
- **com_comprofiler/registers**: User registration form with multi-step fieldsets, validation, captcha support
|
||||
- **com_comprofiler/login**: Login page with remember me checkbox, registration and password recovery links
|
||||
|
||||
#### CSS Architecture (600+ lines)
|
||||
- Mobile-first responsive design with Bootstrap breakpoints (576px, 768px, 992px)
|
||||
- BEM naming convention (`.cb-profile__`, `.cb-userslist__`, `.cb-register__`, `.cb-login__`)
|
||||
- Integrated with template CSS variables for consistent theming
|
||||
- 48px touch targets on mobile, 44px on desktop (WCAG 2.1 Level AA)
|
||||
- 16px input font size on mobile to prevent iOS zoom
|
||||
- Responsive grids adapting from 1 column (mobile) to 2-3 columns (desktop)
|
||||
|
||||
#### Accessibility Features
|
||||
- Full ARIA labels and descriptions for screen readers
|
||||
- Semantic HTML5 structure with proper landmarks
|
||||
- Keyboard navigation support throughout
|
||||
- Required field indicators with visually-hidden labels
|
||||
- Focus states with visible outlines
|
||||
|
||||
#### Security Best Practices
|
||||
- Proper output escaping with htmlspecialchars() and ENT_QUOTES
|
||||
- _JEXEC security checks in all PHP files
|
||||
- index.html protection files in all directories (6 files)
|
||||
- CSRF token support in forms
|
||||
- Input validation and error display
|
||||
|
||||
### Technical Details
|
||||
- **Files Added**: 11 (4 component view files + 6 index.html + 1 root index.html)
|
||||
- **CSS Lines Added**: 600+ lines of responsive styles
|
||||
- **PHP Validation**: All files pass syntax validation
|
||||
- **Component Views**: userprofile, userslist, registers, login
|
||||
- **Documentation**: Ready for MODULE_OVERRIDES.md update
|
||||
|
||||
## [03.07.00] - 2026-02-22
|
||||
|
||||
### Added - Mobile-Responsive Module & Component Overrides
|
||||
|
||||
This major release introduces **20 mobile-responsive module overrides** and **3 component overrides** designed to enhance the mobile user experience across standard Joomla, VirtueMart, Community Builder, and popular third-party extensions.
|
||||
|
||||
#### Search Module
|
||||
- **mod_search**: Mobile-responsive search with multiple button positions (left, right, top, bottom), 48px touch targets, 16px input font to prevent iOS zoom
|
||||
|
||||
#### VirtueMart E-Commerce Modules (5 modules)
|
||||
- **mod_virtuemart_cart**: Shopping cart with responsive product cards, remove buttons, price display
|
||||
- **mod_virtuemart_product**: Product showcase with responsive grid (1-4 columns), hover effects, ratings
|
||||
- **mod_virtuemart_currencies**: Currency selector dropdown with accessible styling
|
||||
- **mod_virtuemart_category**: Category navigation with hierarchical display, product counts
|
||||
- **mod_virtuemart_manufacturer**: Manufacturer/brand display with responsive grid (2-4 columns)
|
||||
- **VIRTUEMART_MODULES_README.md**: Comprehensive master documentation for all VirtueMart overrides
|
||||
|
||||
#### Standard Joomla & Community Builder Modules (6 modules)
|
||||
- **mod_menu**: Main navigation with multiple layout files (default, component, URL), responsive horizontal/vertical layouts
|
||||
- **mod_breadcrumbs**: Breadcrumb navigation with Schema.org markup for SEO
|
||||
- **mod_login**: User login/logout form with 2FA support, remember me checkbox
|
||||
- **mod_articles_latest**: Latest articles with responsive cards, metadata, featured badges
|
||||
- **mod_cblogin**: Community Builder login with avatar display, profile links
|
||||
- **mod_comprofilerOnline**: CB online users with avatar grid, online status indicators
|
||||
- **STANDARD_MODULES_README.md**: Comprehensive master documentation for standard module overrides
|
||||
|
||||
#### Industry Extension Modules (8 modules + 2 components)
|
||||
- **mod_k2_content**: K2 content display with responsive grid (1-3 columns), featured images, metadata
|
||||
- **mod_acymailing**: Newsletter subscription form with validation, GDPR compliance
|
||||
- **mod_hikashop_cart**: HikaShop shopping cart with product list, quantity adjustment
|
||||
- **mod_kunenalatest**: Kunena forum latest posts with excerpts, avatars, reply counts
|
||||
- **mod_kunenalogin**: Kunena forum login with user avatar, statistics, quick login
|
||||
- **mod_kunenasearch**: Kunena forum search with multiple button positions
|
||||
- **mod_kunenastats**: Kunena forum statistics with visual cards, member/topic counts
|
||||
- **mod_osmembership**: OS Membership Pro plans with pricing cards, feature lists, badges
|
||||
- **com_kunena/category**: Kunena forum category list component view
|
||||
- **com_osmembership/plans**: OS Membership Pro responsive pricing table component view
|
||||
- **INDUSTRY_MODULES_README.md**: Comprehensive master documentation for industry extensions
|
||||
|
||||
#### CSS & Styling
|
||||
- Added **2,000+ lines** of mobile-responsive CSS to `src/media/css/template.css`
|
||||
- Four dedicated CSS sections for organized styling:
|
||||
- MOD_SEARCH MOBILE RESPONSIVE STYLES
|
||||
- VIRTUEMART MODULE MOBILE RESPONSIVE STYLES
|
||||
- STANDARD JOOMLA & COMMUNITY BUILDER MODULE STYLES
|
||||
- INDUSTRY EXTENSION MODULE STYLES
|
||||
- ADDITIONAL KUNENA & MEMBERSHIP PRO MODULE STYLES
|
||||
- BEM naming convention for all CSS classes (`.mod-search__button`, `.mod-vm-product__grid`, etc.)
|
||||
- Integration with existing template CSS variables for seamless theming
|
||||
- Responsive grids with Bootstrap-aligned breakpoints (sm, md, lg, xl, xxl)
|
||||
|
||||
#### Documentation
|
||||
- **docs/MODULE_OVERRIDES.md**: Comprehensive guide covering all 23 overrides
|
||||
- Feature descriptions and specifications
|
||||
- CSS architecture and customization guide
|
||||
- Accessibility features documentation
|
||||
- Troubleshooting guide
|
||||
- Best practices and usage examples
|
||||
- Individual README.md files for VirtueMart module groups (5 modules)
|
||||
- Master README files for each category (VirtueMart, Standard, Industry)
|
||||
- Security index.html files in all override directories (23 files)
|
||||
|
||||
### Key Features Across All Overrides
|
||||
|
||||
#### Mobile-First Responsive Design
|
||||
- Touch targets: 48px on mobile, 44px on desktop (WCAG 2.1 compliant)
|
||||
- 16px minimum input font size on mobile (prevents iOS zoom)
|
||||
- Responsive layouts: 1-4 columns based on screen size
|
||||
- Mobile-first CSS with progressive enhancement
|
||||
- Bootstrap-aligned breakpoints: 576px, 768px, 992px, 1200px, 1400px
|
||||
|
||||
#### Accessibility
|
||||
- Full ARIA labels and descriptions on all interactive elements
|
||||
- Keyboard navigation support throughout
|
||||
- Screen reader compatible with semantic HTML5
|
||||
- WCAG 2.1 Level AA compliance
|
||||
- Proper heading hierarchy and focus management
|
||||
- Alternative text for images and icons
|
||||
|
||||
#### Security
|
||||
- Proper output escaping with Joomla escapeHtml()
|
||||
- _JEXEC security checks in all PHP files
|
||||
- index.html protection files in all directories
|
||||
- Input validation where applicable
|
||||
- CSRF token support in forms
|
||||
|
||||
#### Maintainability
|
||||
- BEM naming convention for CSS classes
|
||||
- Consistent code structure across all overrides
|
||||
- Comprehensive inline documentation
|
||||
- Modular, reusable components
|
||||
- Integration with template CSS variables
|
||||
|
||||
### Changed
|
||||
- **Version**: Updated to 03.07.00 across all files
|
||||
|
||||
### Technical Details
|
||||
- **Total Files**: 66 new files created
|
||||
- 42 PHP override files
|
||||
- 23 index.html security files
|
||||
- 1 comprehensive MODULE_OVERRIDES.md documentation
|
||||
- **CSS Added**: 2,000+ lines of responsive styles
|
||||
- **Documentation**: 15,000+ words across all README files
|
||||
|
||||
### Migration Notes
|
||||
- All overrides are opt-in and non-breaking
|
||||
- Existing sites will continue to work without changes
|
||||
- Overrides automatically apply when modules are used
|
||||
- No database changes or migration required
|
||||
- Custom overrides can coexist with template overrides
|
||||
|
||||
### Testing
|
||||
- All PHP syntax validated
|
||||
- Code review completed (all issues resolved)
|
||||
- CodeQL security scan passed
|
||||
- Responsive design tested across breakpoints
|
||||
- Accessibility validated with ARIA compliance
|
||||
|
||||
---
|
||||
|
||||
## [03.06.03] - 2026-01-30
|
||||
|
||||
### Added
|
||||
- **Templates Directory**: Created `/templates/` directory with ready-to-use color palette templates
|
||||
- `colors_custom_light.css` - Comprehensive light mode color template with all available variables
|
||||
- `colors_custom_dark.css` - Comprehensive dark mode color template with all available variables
|
||||
- **CSS Variables Documentation**: Added complete CSS variables reference guide (`docs/CSS_VARIABLES.md`)
|
||||
- Complete list of all customizable CSS variables
|
||||
- Organized by category (colors, typography, borders, etc.)
|
||||
- Usage examples and tips for customization
|
||||
- Light and dark mode variable differences documented
|
||||
|
||||
### Changed
|
||||
- **README**: Updated title to "README - MokoOnyx (VERSION: 03.09.03)"
|
||||
- **README**: Fixed custom color variables instructions with correct file paths
|
||||
- **README**: Updated example CSS variables to use actual template variable names (e.g., `--color-link` instead of `--cassiopeia-color-link`)
|
||||
- **README**: Added note that custom color files are excluded from version control via `.gitignore`
|
||||
- **README**: Enhanced Custom Color Palettes section with step-by-step instructions
|
||||
- **README**: Added link to CSS Variables documentation for complete reference
|
||||
- **TOC CSS**: Updated bootstrap-toc.css to use template color variables for proper theme integration
|
||||
- **Version**: Updated version to 03.06.03 across all files
|
||||
|
||||
### Documentation
|
||||
- **docs/README.md**: Added CSS Variables Reference to developer documentation section
|
||||
- **docs/README.md**: Updated project structure to include `/templates/` directory
|
||||
- **docs/README.md**: Updated version to 03.06.03
|
||||
- Clarified that `colors_custom.css` files are gitignored to prevent fork-specific customizations from being committed
|
||||
|
||||
## [03.06.02] - 2026-01-30
|
||||
|
||||
### Major Rebrand
|
||||
This release includes a complete rebrand from "Moko-Cassiopeia" (hyphenated) to "MokoOnyx" (camelCase).
|
||||
|
||||
### Changed
|
||||
- **Naming Convention**: Changed template identifier from `moko-cassiopeia` to `mokoonyx` across all files
|
||||
- **Display Name**: Updated from "Moko-Cassiopeia" to "MokoOnyx" in all documentation and language files
|
||||
- **Language Constants**: Renamed all language keys from `TPL_MOKO-CASSIOPEIA_*` to `TPL_MOKOONYX_*`
|
||||
- **Language Files**: Renamed from `tpl_moko-cassiopeia.*` to `tpl_mokoonyx.*` (4 files)
|
||||
- **Media Paths**: Updated from `media/templates/site/moko-cassiopeia/` to `media/templates/site/mokoonyx/`
|
||||
- **Repository URLs**: Updated all references to use `MokoOnyx` casing
|
||||
- **Template Element**: Changed Joomla extension element name from `moko-cassiopeia` to `mokoonyx`
|
||||
- **Documentation**: Updated all markdown files, XML manifests, and code comments
|
||||
|
||||
### Removed
|
||||
- **Default Assets**: Removed `logo.svg` and `favicon.ico` to allow clean installations
|
||||
- **Template Overrides**: Removed all template override files (48 files, ~4,500 lines)
|
||||
- Removed `src/templates/html/` folder entirely
|
||||
- Removed overrides for: com_content, com_contact, com_engage, mod_menu, mod_custom, mod_gabble, layouts/chromes
|
||||
- Template now inherits all rendering from Joomla Cassiopeia defaults
|
||||
- Updated `templateDetails.xml` to remove html folder reference
|
||||
|
||||
### Breaking Changes
|
||||
⚠️ **Important**: This release contains breaking changes:
|
||||
- Existing installations will see template name change in Joomla admin
|
||||
- Custom code referencing old language constants (`TPL_MOKO-CASSIOPEIA_*`) will need updates
|
||||
- Custom code referencing old media paths will need updates
|
||||
- Sites relying on custom template overrides will revert to Cassiopeia defaults
|
||||
- Extension element name changed (may require reinstallation in some cases)
|
||||
|
||||
### Migration Notes
|
||||
- Backup your site before upgrading
|
||||
- Review any custom code for references to old naming convention
|
||||
- Test thoroughly after upgrade, especially if using custom overrides
|
||||
|
||||
## [03.06.00] - 2026-01-28
|
||||
|
||||
### Changed
|
||||
- Updated version to 03.06.00 across all files
|
||||
- Standardized version numbering format
|
||||
|
||||
## [03.05.01] - 2026-01-09
|
||||
|
||||
### Added
|
||||
- Added `dependency-review.yml` workflow for dependency vulnerability scanning
|
||||
- Added `standards-compliance.yml` workflow for MokoStandards validation
|
||||
- Added `.github/dependabot.yml` configuration for automated security updates
|
||||
- Added `docs/README.md` as documentation index
|
||||
|
||||
### Changed
|
||||
- Removed custom `codeql-analysis.yml` workflow (repository uses GitHub's default CodeQL setup)
|
||||
- Enforced repository compliance with MokoStandards requirements
|
||||
- Improved security posture with automated scanning and dependency management
|
||||
|
||||
## [03.05.00] - 2026-01-04
|
||||
|
||||
### Added
|
||||
- Created `.github/workflows` directory structure
|
||||
|
||||
### Changed
|
||||
- Replaced `./CODE_OF_CONDUCT.md` from `MokoStandards`
|
||||
- Replaced `./CONTRIBUTING.md` from `MokoStandards`
|
||||
- TODO split to own file
|
||||
|
||||
## [03.01.00] - 2025-12-16
|
||||
|
||||
### Added
|
||||
- Created `.github/workflows/` directory for GitHub Actions
|
||||
|
||||
## [03.00.00] - 2025-12-09
|
||||
|
||||
### Changed
|
||||
- Copyright Headers updated to MokoCodingDefaults standards
|
||||
- Fixed `./templates/mokoonyx/index.php` color style injection
|
||||
- Upgraded Font Awesome 6 to Font Awesome 7 Free
|
||||
- Added Font Awesome 7 Free style fallback
|
||||
|
||||
### Removed
|
||||
- Removed `./CODE_OF_CONDUCT.md` (replaced with MokoStandards version)
|
||||
- Removed `./CONTRIBUTING.md` (replaced with MokoStandards version)
|
||||
|
||||
## [02.01.05] - 2025-09-04
|
||||
|
||||
### Changed
|
||||
- Repaired template.css and colors_standard.css
|
||||
|
||||
### Removed
|
||||
- Removed vmbasic.css
|
||||
|
||||
## [02.00.00] - 2025-08-30
|
||||
|
||||
### Added - Dark Mode Toggle
|
||||
- Frontend toggle switch included in template
|
||||
- JavaScript handles switching between light/dark modes
|
||||
- Dark mode CSS rules applied across template styles
|
||||
- Automatic persistence of user choice (via localStorage)
|
||||
- Admins can override default mode in template settings
|
||||
|
||||
### Added - Header Parameters Update
|
||||
- Added logo parameter support in template settings
|
||||
- Updated metadata & copyright header
|
||||
|
||||
### Added - Expanded TOC (Table of Contents)
|
||||
- Automatic TOC injection when enabled
|
||||
- User selects placement via article > options > layout (`toc-left` or `toc-right`)
|
||||
|
||||
### Changed
|
||||
- Cleaned up `index.php` by removing skip-to-content duplicate calls
|
||||
- Consolidated JavaScript asset loading (ensuring dark-mode script is loaded correctly from external JS file)
|
||||
- Streamlined CSS for toggle switch, ensuring it inherits Bootstrap/Cassiopeia defaults
|
||||
- General accessibility refinements in typography and color contrast
|
||||
- Fixed missing logo param in header output
|
||||
- Corrected stylesheet inconsistencies between Bootstrap 5 helpers and template overrides
|
||||
- Patched redundant calls in script includes
|
||||
|
||||
## [01.00.00] - 2025-01-01
|
||||
|
||||
### Added - Initial Public Release
|
||||
- Font Awesome 6 integration (later upgraded to FA7)
|
||||
- Bootstrap 5 helpers (grid, utility classes)
|
||||
- Automatic Table of Contents (TOC) utility
|
||||
- Moko Expansions: Google Tag Manager / GA4 hooks
|
||||
- Built on top of Joomla's default Cassiopeia template
|
||||
- Minimal core template overrides for maximum upgrade compatibility
|
||||
|
||||
---
|
||||
|
||||
## Links
|
||||
|
||||
- **Full Roadmap**: [MokoOnyx Roadmap](https://mokoconsulting.tech/support/joomla-cms/mokoonyx-roadmap)
|
||||
- **Repository**: [GitHub](https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx)
|
||||
- **Issue Tracker**: [GitHub Issues](https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/issues)
|
||||
|
||||
## Version Format
|
||||
|
||||
This project uses semantic versioning: `MAJOR.MINOR.PATCH`
|
||||
- **MAJOR**: Incompatible API changes or major overhauls
|
||||
- **MINOR**: New features, backwards-compatible
|
||||
- **PATCH**: Bug fixes, backwards-compatible
|
||||
- **Menu icon padding removed** -- Removed hardcoded `p-2` class from all mod_menu icon spans (horizontal + mainmenu layouts); icons now inherit spacing from the parent link
|
||||
- **Runner checkout failures** -- Fixed MySQL deadlocks in Gitea actions scheduler by restarting Gitea and recreating runners with --privileged flag
|
||||
- **workflow_dispatch 500 error** -- Stripped UTF-8 multibyte characters from all YAML files that Gitea's Go parser rejected as control characters
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
|
||||
*
|
||||
* This file is part of a Moko Consulting project.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default menu — Component item layout (sidebar/footer vertical lists)
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Filter\OutputFilter;
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
|
||||
$attributes = [];
|
||||
|
||||
if ($item->anchor_title) {
|
||||
$attributes['title'] = $item->anchor_title;
|
||||
}
|
||||
|
||||
if ($item->anchor_css) {
|
||||
$attributes['class'] = $item->anchor_css;
|
||||
}
|
||||
|
||||
if ($item->anchor_rel) {
|
||||
$attributes['rel'] = $item->anchor_rel;
|
||||
}
|
||||
|
||||
$linktype = $item->title;
|
||||
|
||||
if ($item->menu_icon) {
|
||||
if ($itemParams->get('menu_text', 1)) {
|
||||
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
|
||||
} else {
|
||||
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span><span class="visually-hidden">' . $item->title . '</span>';
|
||||
}
|
||||
}
|
||||
|
||||
if ($item->browserNav == 1) {
|
||||
$attributes['target'] = '_blank';
|
||||
$attributes['rel'] = 'noopener noreferrer';
|
||||
} elseif ($item->browserNav == 2) {
|
||||
$options = 'toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,resizable=yes,' . $params->get('window_open');
|
||||
$attributes['onclick'] = "window.open(this.href, 'targetWindow', '" . $options . "'); return false;";
|
||||
}
|
||||
|
||||
$attributes['class'] = 'nav-link mod-menu__link';
|
||||
|
||||
echo HTMLHelper::_('link', OutputFilter::ampReplace(htmlspecialchars($item->flink, ENT_COMPAT, 'UTF-8', false)), $linktype, $attributes);
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
|
||||
*
|
||||
* This file is part of a Moko Consulting project.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default menu — Heading item layout (sidebar/footer vertical lists)
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
$title = $item->anchor_title ? ' title="' . $item->anchor_title . '"' : '';
|
||||
$anchor_css = $item->anchor_css ?: '';
|
||||
|
||||
$linktype = $item->title;
|
||||
|
||||
if ($item->menu_icon) {
|
||||
if ($itemParams->get('menu_text', 1)) {
|
||||
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
|
||||
} else {
|
||||
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span><span class="visually-hidden">' . $item->title . '</span>';
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
<span class="nav-link mod-menu__heading <?php echo $anchor_css; ?>"<?php echo $title; ?>><?php echo $linktype; ?></span>
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
|
||||
*
|
||||
* This file is part of a Moko Consulting project.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default menu — Separator item layout (sidebar/footer vertical lists)
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
$title = $item->anchor_title ? ' title="' . $item->anchor_title . '"' : '';
|
||||
$anchor_css = $item->anchor_css ?: '';
|
||||
|
||||
$linktype = $item->title;
|
||||
|
||||
if ($item->menu_icon) {
|
||||
if ($itemParams->get('menu_text', 1)) {
|
||||
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
|
||||
} else {
|
||||
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span><span class="visually-hidden">' . $item->title . '</span>';
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
<hr class="mod-menu__separator <?php echo $anchor_css; ?>"<?php echo $title; ?> />
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
|
||||
*
|
||||
* This file is part of a Moko Consulting project.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default menu — URL item layout (sidebar/footer vertical lists)
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Filter\OutputFilter;
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
|
||||
$attributes = [];
|
||||
|
||||
if ($item->anchor_title) {
|
||||
$attributes['title'] = $item->anchor_title;
|
||||
}
|
||||
|
||||
if ($item->anchor_css) {
|
||||
$attributes['class'] = $item->anchor_css;
|
||||
}
|
||||
|
||||
if ($item->anchor_rel) {
|
||||
$attributes['rel'] = $item->anchor_rel;
|
||||
}
|
||||
|
||||
$linktype = $item->title;
|
||||
|
||||
if ($item->menu_icon) {
|
||||
if ($itemParams->get('menu_text', 1)) {
|
||||
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
|
||||
} else {
|
||||
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span><span class="visually-hidden">' . $item->title . '</span>';
|
||||
}
|
||||
}
|
||||
|
||||
if ($item->browserNav == 1) {
|
||||
$attributes['target'] = '_blank';
|
||||
$attributes['rel'] = 'noopener noreferrer';
|
||||
} elseif ($item->browserNav == 2) {
|
||||
$options = 'toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,resizable=yes,' . $params->get('window_open');
|
||||
$attributes['onclick'] = "window.open(this.href, 'targetWindow', '" . $options . "'); return false;";
|
||||
}
|
||||
|
||||
$linkClass = 'nav-link mod-menu__link';
|
||||
|
||||
if (isset($attributes['class'])) {
|
||||
$attributes['class'] .= ' ' . $linkClass;
|
||||
} else {
|
||||
$attributes['class'] = $linkClass;
|
||||
}
|
||||
|
||||
echo HTMLHelper::_('link', OutputFilter::ampReplace(htmlspecialchars($item->flink, ENT_COMPAT, 'UTF-8', false)), $linktype, $attributes);
|
||||
@@ -8,8 +8,8 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Main Menu - Mobile responsive collapsible dropdown menu override
|
||||
* Bootstrap 5 responsive navbar with hamburger menu
|
||||
* Horizontal menu — always-visible inline links that wrap on mobile.
|
||||
* No hamburger, no collapse. Suitable for Quick Links, utility nav, topbar menus.
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
@@ -22,19 +22,13 @@ if ($tagId = $params->get('tag_id', '')) {
|
||||
$id = ' id="' . $tagId . '"';
|
||||
}
|
||||
|
||||
// Get module class suffix
|
||||
$moduleclass_sfx = htmlspecialchars($params->get('moduleclass_sfx', ''), ENT_COMPAT, 'UTF-8');
|
||||
|
||||
// The menu class is deprecated. Use mod-menu instead
|
||||
?>
|
||||
<nav class="mod-menu mod-menu-main navbar navbar-expand-lg<?php echo $moduleclass_sfx; ?>"<?php echo $id; ?>>
|
||||
<div class="container-fluid p-0">
|
||||
<!-- Collapsible menu content — toggle controlled by .nav-mobile-bar in index.php -->
|
||||
<div class="collapse navbar-collapse" id="moko-main-menu-collapse">
|
||||
<ul class="navbar-nav mod-menu-main__list">
|
||||
<nav class="mod-menu mod-menu-horizontal<?php echo $moduleclass_sfx; ?>"<?php echo $id; ?> aria-label="<?php echo htmlspecialchars($module->title, ENT_COMPAT, 'UTF-8'); ?>">
|
||||
<ul class="nav mod-menu-horizontal__list flex-wrap">
|
||||
<?php foreach ($list as $i => &$item) :
|
||||
$itemParams = $item->getParams();
|
||||
$class = 'nav-item mod-menu-main__item item-' . $item->id;
|
||||
$class = 'nav-item mod-menu-horizontal__item item-' . $item->id;
|
||||
|
||||
if ($item->id == $default_id) {
|
||||
$class .= ' default';
|
||||
@@ -83,19 +77,14 @@ $moduleclass_sfx = htmlspecialchars($params->get('moduleclass_sfx', ''), ENT_COM
|
||||
break;
|
||||
endswitch;
|
||||
|
||||
// The next item is deeper.
|
||||
if ($item->deeper) {
|
||||
echo '<ul class="dropdown-menu mod-menu-main__dropdown">';
|
||||
echo '<ul class="dropdown-menu mod-menu-horizontal__dropdown">';
|
||||
} elseif ($item->shallower) {
|
||||
// The next item is shallower.
|
||||
echo '</li>';
|
||||
echo str_repeat('</ul></li>', $item->level_diff);
|
||||
} else {
|
||||
// The next item is on the same level.
|
||||
echo '</li>';
|
||||
}
|
||||
endforeach;
|
||||
?></ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Main Menu - Component item layout
|
||||
* Horizontal menu — Component item layout
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
@@ -33,12 +33,9 @@ if ($item->anchor_rel) {
|
||||
$linktype = $item->title;
|
||||
|
||||
if ($item->menu_icon) {
|
||||
// The link is an icon
|
||||
if ($itemParams->get('menu_text', 1)) {
|
||||
// If the link text is to be displayed, the icon is added with aria-hidden
|
||||
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
|
||||
} else {
|
||||
// If the icon itself is the link, it needs a visually hidden text
|
||||
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span><span class="visually-hidden">' . $item->title . '</span>';
|
||||
}
|
||||
}
|
||||
@@ -48,12 +45,10 @@ if ($item->browserNav == 1) {
|
||||
$attributes['rel'] = 'noopener noreferrer';
|
||||
} elseif ($item->browserNav == 2) {
|
||||
$options = 'toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,resizable=yes,' . $params->get('window_open');
|
||||
|
||||
$attributes['onclick'] = "window.open(this.href, 'targetWindow', '" . $options . "'); return false;";
|
||||
}
|
||||
|
||||
// Add dropdown toggle for items with children
|
||||
$linkClass = 'nav-link mod-menu-main__link';
|
||||
$linkClass = 'nav-link mod-menu-horizontal__link';
|
||||
if ($item->deeper) {
|
||||
$linkClass .= ' dropdown-toggle';
|
||||
$attributes['data-bs-toggle'] = 'dropdown';
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Main Menu - Heading item layout
|
||||
* Horizontal menu — Heading item layout
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
@@ -19,18 +19,14 @@ $anchor_css = $item->anchor_css ?: '';
|
||||
$linktype = $item->title;
|
||||
|
||||
if ($item->menu_icon) {
|
||||
// The link is an icon
|
||||
if ($itemParams->get('menu_text', 1)) {
|
||||
// If the link text is to be displayed, the icon is added with aria-hidden
|
||||
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
|
||||
} else {
|
||||
// If the icon itself is the link, it needs a visually hidden text
|
||||
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span><span class="visually-hidden">' . $item->title . '</span>';
|
||||
}
|
||||
}
|
||||
|
||||
// Add dropdown toggle for items with children
|
||||
$headingClass = 'nav-link mod-menu-main__heading';
|
||||
$headingClass = 'nav-link mod-menu-horizontal__heading';
|
||||
if ($item->deeper) {
|
||||
$headingClass .= ' dropdown-toggle';
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Main Menu - Separator item layout
|
||||
* Horizontal menu — Separator item layout
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
@@ -19,15 +19,12 @@ $anchor_css = $item->anchor_css ?: '';
|
||||
$linktype = $item->title;
|
||||
|
||||
if ($item->menu_icon) {
|
||||
// The link is an icon
|
||||
if ($itemParams->get('menu_text', 1)) {
|
||||
// If the link text is to be displayed, the icon is added with aria-hidden
|
||||
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
|
||||
} else {
|
||||
// If the icon itself is the link, it needs a visually hidden text
|
||||
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span><span class="visually-hidden">' . $item->title . '</span>';
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
<span class="dropdown-divider mod-menu-main__separator <?php echo $anchor_css; ?>"<?php echo $title; ?>><?php echo $linktype; ?></span>
|
||||
<span class="dropdown-divider mod-menu-horizontal__separator <?php echo $anchor_css; ?>"<?php echo $title; ?>><?php echo $linktype; ?></span>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Main Menu - URL item layout
|
||||
* Horizontal menu — URL item layout
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
@@ -33,12 +33,9 @@ if ($item->anchor_rel) {
|
||||
$linktype = $item->title;
|
||||
|
||||
if ($item->menu_icon) {
|
||||
// The link is an icon
|
||||
if ($itemParams->get('menu_text', 1)) {
|
||||
// If the link text is to be displayed, the icon is added with aria-hidden
|
||||
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
|
||||
} else {
|
||||
// If the icon itself is the link, it needs a visually hidden text
|
||||
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span><span class="visually-hidden">' . $item->title . '</span>';
|
||||
}
|
||||
}
|
||||
@@ -48,12 +45,10 @@ if ($item->browserNav == 1) {
|
||||
$attributes['rel'] = 'noopener noreferrer';
|
||||
} elseif ($item->browserNav == 2) {
|
||||
$options = 'toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,resizable=yes,' . $params->get('window_open');
|
||||
|
||||
$attributes['onclick'] = "window.open(this.href, 'targetWindow', '" . $options . "'); return false;";
|
||||
}
|
||||
|
||||
// Add dropdown toggle for items with children
|
||||
$linkClass = 'nav-link mod-menu-main__link';
|
||||
$linkClass = 'nav-link mod-menu-horizontal__link';
|
||||
if ($item->deeper) {
|
||||
$linkClass .= ' dropdown-toggle';
|
||||
$attributes['data-bs-toggle'] = 'dropdown';
|
||||
@@ -61,7 +56,6 @@ if ($item->deeper) {
|
||||
$attributes['aria-expanded'] = 'false';
|
||||
}
|
||||
|
||||
// Merge existing class with our class
|
||||
if (isset($attributes['class'])) {
|
||||
$attributes['class'] .= ' ' . $linkClass;
|
||||
} else {
|
||||
|
||||
@@ -28,14 +28,9 @@ $moduleclass_sfx = htmlspecialchars($params->get('moduleclass_sfx', ''), ENT_COM
|
||||
// The menu class is deprecated. Use mod-menu instead
|
||||
?>
|
||||
<nav class="mod-menu mod-menu-main navbar navbar-expand-lg<?php echo $moduleclass_sfx; ?>"<?php echo $id; ?>>
|
||||
<div class="container-fluid">
|
||||
<!-- Hamburger toggle button for mobile -->
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mainMenuCollapse-<?php echo $module->id; ?>" aria-controls="mainMenuCollapse-<?php echo $module->id; ?>" aria-expanded="false" aria-label="Toggle Main Menu">
|
||||
<span class="fa-solid fa-bars" aria-hidden="true"></span>
|
||||
</button>
|
||||
|
||||
<!-- Collapsible menu content -->
|
||||
<div class="collapse navbar-collapse" id="mainMenuCollapse-<?php echo $module->id; ?>">
|
||||
<div class="container-fluid p-0">
|
||||
<!-- Collapsible menu content — toggle controlled by .nav-mobile-bar in index.php -->
|
||||
<div class="collapse navbar-collapse" id="moko-main-menu-collapse">
|
||||
<ul class="navbar-nav mod-menu-main__list">
|
||||
<?php foreach ($list as $i => &$item) :
|
||||
$itemParams = $item->getParams();
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
;
|
||||
; SPDX-License-Identifier: GPL-3.0-or-later
|
||||
;
|
||||
TPL_MOKOONYX="MokoOnyx"
|
||||
TPL_MOKOONYX="Template - MokoOnyx"
|
||||
TPL_MOKOONYX_GOOGLE_FIELDSET_LABEL="Google"
|
||||
TPL_MOKOONYX_DRAWERS_FIELDSET_LABEL="Drawers"
|
||||
TPL_MOKOONYX_MOD_MENU_LAYOUT_COLLAPSE_METISMENU="Collapsible Dropdown"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
;
|
||||
; SPDX-License-Identifier: GPL-3.0-or-later
|
||||
;
|
||||
TPL_MOKOONYX="MokoOnyx"
|
||||
TPL_MOKOONYX="Template - MokoOnyx"
|
||||
TPL_MOKOONYX_GOOGLE_FIELDSET_LABEL="Google"
|
||||
TPL_MOKOONYX_DRAWERS_FIELDSET_LABEL="Drawers"
|
||||
TPL_MOKOONYX_MOD_MENU_LAYOUT_COLLAPSE_METISMENU="Collapsible Dropdown"
|
||||
|
||||
@@ -23506,3 +23506,7 @@ padding: 0.25rem;
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
}
|
||||
|
||||
.fa-solid {
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
|
||||
@@ -35,8 +35,8 @@
|
||||
https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/raw/branch/main/updates.xml
|
||||
</server>
|
||||
</updateservers>
|
||||
<name>MokoOnyx</name>
|
||||
<version>02.07.00</version>
|
||||
<name>Template - MokoOnyx</name>
|
||||
<version>02.08.00</version>
|
||||
<scriptfile>script.php</scriptfile>
|
||||
<creationDate>2026-05-16</creationDate>
|
||||
<author>Jonathan Miller || Moko Consulting</author>
|
||||
@@ -366,3 +366,4 @@
|
||||
</config>
|
||||
</extension>
|
||||
<!-- dev release 01.00.26 -->
|
||||
|
||||
|
||||
+49
-44
@@ -1,93 +1,98 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<!-- Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
VERSION: 02.07.00
|
||||
VERSION: 02.08.00
|
||||
-->
|
||||
|
||||
<updates>
|
||||
<update>
|
||||
<name>MokoOnyx</name>
|
||||
<description>MokoOnyx update</description>
|
||||
<name>Template - MokoOnyx</name>
|
||||
<description>Template - MokoOnyx development build.</description>
|
||||
<element>mokoonyx</element>
|
||||
<type>template</type>
|
||||
<version>02.07.00</version>
|
||||
<client>site</client>
|
||||
<tags><tag>development</tag></tags>
|
||||
<infourl title="MokoOnyx">https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/stable</infourl>
|
||||
<version>02.08.00</version>
|
||||
<creationDate>2026-05-29</creationDate>
|
||||
<infourl title='Template - MokoOnyx'>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/stable</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/stable/tpl_mokoonyx-02.07.00.zip</downloadurl>
|
||||
<downloadurl type='full' format='zip'>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/stable/tpl_mokoonyx-02.08.00.zip</downloadurl>
|
||||
</downloads>
|
||||
<sha256>8961a292d1f230b3870a54106b0dc823ca2f33477cc09fe079370efb7ffe2ab1</sha256>
|
||||
<targetplatform name="joomla" version="((5.[0-9])|(6.[0-9]))" />
|
||||
<sha256>a36b32b3f2df027ff11dc6b79c1c4ae98d684e2683bc2db25d303fe6c96ca644</sha256>
|
||||
<tags><tag>dev</tag></tags>
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
<targetplatform name="joomla" version="(5|6)\..*" />
|
||||
</update>
|
||||
<update>
|
||||
<name>MokoOnyx</name>
|
||||
<description>MokoOnyx update</description>
|
||||
<name>Template - MokoOnyx</name>
|
||||
<description>Template - MokoOnyx alpha build.</description>
|
||||
<element>mokoonyx</element>
|
||||
<type>template</type>
|
||||
<version>02.07.00</version>
|
||||
<client>site</client>
|
||||
<version>02.08.00</version>
|
||||
<creationDate>2026-05-29</creationDate>
|
||||
<infourl title='Template - MokoOnyx'>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/stable</infourl>
|
||||
<downloads>
|
||||
<downloadurl type='full' format='zip'>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/stable/tpl_mokoonyx-02.08.00.zip</downloadurl>
|
||||
</downloads>
|
||||
<sha256>a36b32b3f2df027ff11dc6b79c1c4ae98d684e2683bc2db25d303fe6c96ca644</sha256>
|
||||
<tags><tag>alpha</tag></tags>
|
||||
<infourl title="MokoOnyx">https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/stable</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/stable/tpl_mokoonyx-02.07.00.zip</downloadurl>
|
||||
</downloads>
|
||||
<sha256>8961a292d1f230b3870a54106b0dc823ca2f33477cc09fe079370efb7ffe2ab1</sha256>
|
||||
<targetplatform name="joomla" version="((5.[0-9])|(6.[0-9]))" />
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
<targetplatform name="joomla" version="(5|6)\..*" />
|
||||
</update>
|
||||
<update>
|
||||
<name>MokoOnyx</name>
|
||||
<description>MokoOnyx update</description>
|
||||
<name>Template - MokoOnyx</name>
|
||||
<description>Template - MokoOnyx beta build.</description>
|
||||
<element>mokoonyx</element>
|
||||
<type>template</type>
|
||||
<version>02.07.00</version>
|
||||
<client>site</client>
|
||||
<version>02.08.00</version>
|
||||
<creationDate>2026-05-29</creationDate>
|
||||
<infourl title='Template - MokoOnyx'>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/stable</infourl>
|
||||
<downloads>
|
||||
<downloadurl type='full' format='zip'>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/stable/tpl_mokoonyx-02.08.00.zip</downloadurl>
|
||||
</downloads>
|
||||
<sha256>a36b32b3f2df027ff11dc6b79c1c4ae98d684e2683bc2db25d303fe6c96ca644</sha256>
|
||||
<tags><tag>beta</tag></tags>
|
||||
<infourl title="MokoOnyx">https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/stable</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/stable/tpl_mokoonyx-02.07.00.zip</downloadurl>
|
||||
</downloads>
|
||||
<sha256>8961a292d1f230b3870a54106b0dc823ca2f33477cc09fe079370efb7ffe2ab1</sha256>
|
||||
<targetplatform name="joomla" version="((5.[0-9])|(6.[0-9]))" />
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
<targetplatform name="joomla" version="(5|6)\..*" />
|
||||
</update>
|
||||
<update>
|
||||
<name>MokoOnyx</name>
|
||||
<description>MokoOnyx update</description>
|
||||
<name>Template - MokoOnyx</name>
|
||||
<description>Template - MokoOnyx rc build.</description>
|
||||
<element>mokoonyx</element>
|
||||
<type>template</type>
|
||||
<version>02.07.00</version>
|
||||
<client>site</client>
|
||||
<version>02.08.00</version>
|
||||
<creationDate>2026-05-29</creationDate>
|
||||
<infourl title='Template - MokoOnyx'>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/stable</infourl>
|
||||
<downloads>
|
||||
<downloadurl type='full' format='zip'>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/stable/tpl_mokoonyx-02.08.00.zip</downloadurl>
|
||||
</downloads>
|
||||
<sha256>a36b32b3f2df027ff11dc6b79c1c4ae98d684e2683bc2db25d303fe6c96ca644</sha256>
|
||||
<tags><tag>rc</tag></tags>
|
||||
<infourl title="MokoOnyx">https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/stable</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/stable/tpl_mokoonyx-02.07.00.zip</downloadurl>
|
||||
</downloads>
|
||||
<sha256>8961a292d1f230b3870a54106b0dc823ca2f33477cc09fe079370efb7ffe2ab1</sha256>
|
||||
<targetplatform name="joomla" version="((5.[0-9])|(6.[0-9]))" />
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
<targetplatform name="joomla" version="(5|6)\..*" />
|
||||
</update>
|
||||
<update>
|
||||
<name>MokoOnyx</name>
|
||||
<description>MokoOnyx update</description>
|
||||
<name>Template - MokoOnyx</name>
|
||||
<description>Template - MokoOnyx stable build.</description>
|
||||
<element>mokoonyx</element>
|
||||
<type>template</type>
|
||||
<version>02.07.00</version>
|
||||
<client>site</client>
|
||||
<tags><tag>stable</tag></tags>
|
||||
<infourl title="MokoOnyx">https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/stable</infourl>
|
||||
<version>02.08.00</version>
|
||||
<creationDate>2026-05-29</creationDate>
|
||||
<infourl title='Template - MokoOnyx'>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/stable</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/stable/tpl_mokoonyx-02.07.00.zip</downloadurl>
|
||||
<downloadurl type='full' format='zip'>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/stable/tpl_mokoonyx-02.08.00.zip</downloadurl>
|
||||
</downloads>
|
||||
<sha256>8961a292d1f230b3870a54106b0dc823ca2f33477cc09fe079370efb7ffe2ab1</sha256>
|
||||
<targetplatform name="joomla" version="((5.[0-9])|(6.[0-9]))" />
|
||||
<sha256>a36b32b3f2df027ff11dc6b79c1c4ae98d684e2683bc2db25d303fe6c96ca644</sha256>
|
||||
<tags><tag>stable</tag></tags>
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
<targetplatform name="joomla" version="(5|6)\..*" />
|
||||
</update>
|
||||
</updates>
|
||||
|
||||
Reference in New Issue
Block a user