Merge dev → main: MokoCassiopeia retired, MokoOnyx migration
Some checks failed
Repo Health / Access control (push) Successful in 1s
Standards Compliance / Secret Scanning (push) Successful in 8s
Standards Compliance / License Header Validation (push) Successful in 6s
Standards Compliance / Repository Structure Validation (push) Successful in 6s
Standards Compliance / Coding Standards Check (push) Failing after 4s
Standards Compliance / Version Consistency Check (push) Failing after 3s
Standards Compliance / Workflow Configuration Check (push) Failing after 3s
Standards Compliance / Documentation Quality Check (push) Successful in 3s
Standards Compliance / README Completeness Check (push) Successful in 2s
Standards Compliance / Git Repository Hygiene (push) Successful in 3s
Standards Compliance / Line Length Check (push) Failing after 4s
Standards Compliance / File Naming Standards (push) Successful in 4s
Standards Compliance / Insecure Code Pattern Detection (push) Successful in 2s
Standards Compliance / Script Integrity Validation (push) Successful in 19s
Standards Compliance / Code Duplication Detection (push) Failing after 3s
Standards Compliance / Code Complexity Analysis (push) Successful in 4s
Standards Compliance / File Size Limits (push) Successful in 3s
Standards Compliance / Dead Code Detection (push) Successful in 3s
Standards Compliance / TODO/FIXME Tracking (push) Successful in 3s
Standards Compliance / Binary File Detection (push) Successful in 4s
Standards Compliance / Dependency Vulnerability Scanning (push) Failing after 3s
Standards Compliance / Broken Link Detection (push) Successful in 4s
Standards Compliance / Unused Dependencies Check (push) Successful in 7s
Standards Compliance / API Documentation Coverage (push) Successful in 2s
Standards Compliance / Accessibility Check (push) Successful in 3s
Standards Compliance / Performance Metrics (push) Successful in 3s
Standards Compliance / Repository Health Check (push) Failing after 2s
Standards Compliance / Enterprise Readiness Check (push) Failing after 3s
CodeQL Security Scanning / Analyze (javascript) (push) Failing after 1m23s
Repo Health / Release configuration (push) Failing after 4s
Repo Health / Scripts governance (push) Successful in 4s
Standards Compliance / Terraform Configuration Validation (push) Successful in 7s
Repo Health / Repository health (push) Failing after 4s
Standards Compliance / Compliance Summary (push) Failing after 1s
CodeQL Security Scanning / Analyze (actions) (push) Failing after 12m23s
CodeQL Security Scanning / Security Scan Summary (push) Successful in 1s
Some checks failed
Repo Health / Access control (push) Successful in 1s
Standards Compliance / Secret Scanning (push) Successful in 8s
Standards Compliance / License Header Validation (push) Successful in 6s
Standards Compliance / Repository Structure Validation (push) Successful in 6s
Standards Compliance / Coding Standards Check (push) Failing after 4s
Standards Compliance / Version Consistency Check (push) Failing after 3s
Standards Compliance / Workflow Configuration Check (push) Failing after 3s
Standards Compliance / Documentation Quality Check (push) Successful in 3s
Standards Compliance / README Completeness Check (push) Successful in 2s
Standards Compliance / Git Repository Hygiene (push) Successful in 3s
Standards Compliance / Line Length Check (push) Failing after 4s
Standards Compliance / File Naming Standards (push) Successful in 4s
Standards Compliance / Insecure Code Pattern Detection (push) Successful in 2s
Standards Compliance / Script Integrity Validation (push) Successful in 19s
Standards Compliance / Code Duplication Detection (push) Failing after 3s
Standards Compliance / Code Complexity Analysis (push) Successful in 4s
Standards Compliance / File Size Limits (push) Successful in 3s
Standards Compliance / Dead Code Detection (push) Successful in 3s
Standards Compliance / TODO/FIXME Tracking (push) Successful in 3s
Standards Compliance / Binary File Detection (push) Successful in 4s
Standards Compliance / Dependency Vulnerability Scanning (push) Failing after 3s
Standards Compliance / Broken Link Detection (push) Successful in 4s
Standards Compliance / Unused Dependencies Check (push) Successful in 7s
Standards Compliance / API Documentation Coverage (push) Successful in 2s
Standards Compliance / Accessibility Check (push) Successful in 3s
Standards Compliance / Performance Metrics (push) Successful in 3s
Standards Compliance / Repository Health Check (push) Failing after 2s
Standards Compliance / Enterprise Readiness Check (push) Failing after 3s
CodeQL Security Scanning / Analyze (javascript) (push) Failing after 1m23s
Repo Health / Release configuration (push) Failing after 4s
Repo Health / Scripts governance (push) Successful in 4s
Standards Compliance / Terraform Configuration Validation (push) Successful in 7s
Repo Health / Repository health (push) Failing after 4s
Standards Compliance / Compliance Summary (push) Failing after 1s
CodeQL Security Scanning / Analyze (actions) (push) Failing after 12m23s
CodeQL Security Scanning / Security Scan Summary (push) Successful in 1s
This commit is contained in:
42
.github/CLAUDE.md
vendored
42
.github/CLAUDE.md
vendored
@@ -115,38 +115,34 @@ BRIEF: One-line description
|
||||
|
||||
**`README.md` is the single source of truth for the repository version.**
|
||||
|
||||
- **Bump the patch version on every PR** — increment `XX.YY.ZZ` (e.g. `01.02.03` → `01.02.04`) in `README.md` before opening the PR; the `sync-version-on-merge` workflow propagates it automatically to all badges and `FILE INFORMATION` headers on merge to `main`.
|
||||
- **Patch version is auto-bumped by the release workflow** — `release.yml` reads the current version from `README.md`, increments the patch (`XX.YY.ZZ` → `XX.YY.(ZZ+1)`), updates `README.md`, `templateDetails.xml`, and the matching channel in `updates.xml`, commits, pushes, then builds the ZIP. Manual bumping is no longer required.
|
||||
- The `VERSION: XX.YY.ZZ` field in `README.md` governs all other version references.
|
||||
- Version format is zero-padded semver: `XX.YY.ZZ` (e.g. `01.02.03`).
|
||||
- Never hardcode a specific version in document body text — use the badge or FILE INFORMATION header only.
|
||||
|
||||
### Joomla Version Alignment
|
||||
|
||||
The version in `README.md` **must always match** the `<version>` tag in `manifest.xml` and the latest entry in `updates.xml`. The `make release` command / release workflow updates all three automatically.
|
||||
The version in `README.md` **must always match** the `<version>` tag in `templateDetails.xml` and the matching channel entry in `updates.xml`. The release workflow updates all three automatically.
|
||||
|
||||
### Multi-Channel updates.xml
|
||||
|
||||
`updates.xml` contains separate `<update>` blocks per stability channel (development, alpha, beta, rc, stable). Each release workflow only modifies its own channel using targeted Python regex replacement — other channels are preserved untouched. Joomla filters by the user's "Minimum Stability" setting.
|
||||
|
||||
```xml
|
||||
<!-- In manifest.xml — must match README.md version -->
|
||||
<version>01.02.04</version>
|
||||
|
||||
<!-- In updates.xml — prepend a new <update> block for every release.
|
||||
Note: the backslash in version="4\.[0-9]+" is a literal backslash character
|
||||
in the XML attribute value. Joomla's update server treats the value as a
|
||||
regular expression, so \. matches a literal dot. -->
|
||||
<updates>
|
||||
<update>
|
||||
<name>{{EXTENSION_NAME}}</name>
|
||||
<version>01.02.04</version>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">
|
||||
https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/download/01.02.04/{{EXTENSION_ELEMENT}}-01.02.04.zip
|
||||
</downloadurl>
|
||||
</downloads>
|
||||
<targetplatform name="joomla" version="4\.[0-9]+" />
|
||||
</update>
|
||||
<!-- … older entries preserved below … -->
|
||||
<!-- 1. DEVELOPMENT --> <update>...<tag>development</tag>...</update>
|
||||
<!-- 2. ALPHA --> <update>...<tag>alpha</tag>...</update>
|
||||
<!-- 3. BETA --> <update>...<tag>beta</tag>...</update>
|
||||
<!-- 4. RC --> <update>...<tag>rc</tag>...</update>
|
||||
<!-- 5. STABLE --> <update>...<tag>stable</tag>...</update>
|
||||
</updates>
|
||||
```
|
||||
|
||||
**Key rules:**
|
||||
- SHA-256 must be raw hex (no `sha256:` prefix)
|
||||
- Version format must be `XX.YY.ZZ`, not tag names like `v01`
|
||||
- Download URLs must point to Gitea (not GitHub) for all pre-release channels
|
||||
|
||||
---
|
||||
|
||||
## Joomla Extension Structure
|
||||
@@ -286,11 +282,11 @@ Approved prefixes: `dev/` · `rc/` · `version/` · `patch/` · `copilot/` · `d
|
||||
| Change type | Documentation to update |
|
||||
|-------------|------------------------|
|
||||
| New or renamed PHP class/method | PHPDoc block; `docs/api/` entry |
|
||||
| New or changed manifest.xml | Update `updates.xml` version; bump README.md version |
|
||||
| New release | Prepend `<update>` block to `updates.xml`; update CHANGELOG.md; bump README.md version |
|
||||
| New or changed manifest.xml | Release workflow auto-bumps version across README.md, templateDetails.xml, and updates.xml |
|
||||
| New release | Trigger `release.yml` — auto-bumps patch, builds ZIP, updates matching channel in `updates.xml` |
|
||||
| New or changed workflow | `docs/workflows/<workflow-name>.md` |
|
||||
| Any modified file | Update the `VERSION` field in that file's `FILE INFORMATION` block |
|
||||
| **Every PR** | **Bump the patch version** — increment `XX.YY.ZZ` in `README.md`; `sync-version-on-merge` propagates it |
|
||||
| **Every release** | **Patch auto-bumped** by `release.yml` — no manual version bump needed |
|
||||
|
||||
---
|
||||
|
||||
|
||||
20
.github/workflows/auto-release.yml
vendored
20
.github/workflows/auto-release.yml
vendored
@@ -26,7 +26,7 @@
|
||||
# | 8. Build ZIP, upload asset, write SHA-256 to updates.xml |
|
||||
# | |
|
||||
# | Every version change: archives main -> version/XX.YY branch |
|
||||
# | Patch 00 = development (no release). First release = patch 01. |
|
||||
# | All patches release (including 00). Patch 00/01 = full pipeline. |
|
||||
# | First release only (patch == 01): |
|
||||
# | 7b. Create new GitHub Release |
|
||||
# | |
|
||||
@@ -100,19 +100,13 @@ jobs:
|
||||
echo "minor=$MINOR" >> "$GITHUB_OUTPUT"
|
||||
echo "major=$MAJOR" >> "$GITHUB_OUTPUT"
|
||||
echo "release_tag=v${MAJOR}" >> "$GITHUB_OUTPUT"
|
||||
if [ "$PATCH" = "00" ]; then
|
||||
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||
echo "is_minor=false" >> "$GITHUB_OUTPUT"
|
||||
echo "Version: $VERSION (patch 00 = development — skipping release)"
|
||||
echo "skip=false" >> "$GITHUB_OUTPUT"
|
||||
if [ "$PATCH" = "00" ] || [ "$PATCH" = "01" ]; then
|
||||
echo "is_minor=true" >> "$GITHUB_OUTPUT"
|
||||
echo "Version: $VERSION (first release for this minor — full pipeline)"
|
||||
else
|
||||
echo "skip=false" >> "$GITHUB_OUTPUT"
|
||||
if [ "$PATCH" = "01" ]; then
|
||||
echo "is_minor=true" >> "$GITHUB_OUTPUT"
|
||||
echo "Version: $VERSION (first release — full pipeline)"
|
||||
else
|
||||
echo "is_minor=false" >> "$GITHUB_OUTPUT"
|
||||
echo "Version: $VERSION (patch — platform version + badges only)"
|
||||
fi
|
||||
echo "is_minor=false" >> "$GITHUB_OUTPUT"
|
||||
echo "Version: $VERSION (patch — platform version + badges only)"
|
||||
fi
|
||||
|
||||
- name: Check if already released
|
||||
|
||||
79
.github/workflows/auto-update-sha.yml
vendored
79
.github/workflows/auto-update-sha.yml
vendored
@@ -70,33 +70,60 @@ jobs:
|
||||
echo "sha256=${SHA256_HASH}" >> $GITHUB_OUTPUT
|
||||
echo "SHA-256 Hash: ${SHA256_HASH}"
|
||||
|
||||
- name: Update updates.xml
|
||||
- name: Determine stability channel
|
||||
id: channel
|
||||
run: |
|
||||
TAG="${{ steps.tag.outputs.tag }}"
|
||||
SHA256="${{ steps.sha.outputs.sha256 }}"
|
||||
case "$TAG" in
|
||||
development) STABILITY="development" ;;
|
||||
alpha) STABILITY="alpha" ;;
|
||||
beta) STABILITY="beta" ;;
|
||||
release-candidate) STABILITY="rc" ;;
|
||||
*) STABILITY="stable" ;;
|
||||
esac
|
||||
echo "stability=${STABILITY}" >> $GITHUB_OUTPUT
|
||||
echo "Channel: ${STABILITY}"
|
||||
|
||||
- name: Update updates.xml (targeted channel only)
|
||||
env:
|
||||
PY_TAG: ${{ steps.tag.outputs.tag }}
|
||||
PY_SHA: ${{ steps.sha.outputs.sha256 }}
|
||||
PY_STABILITY: ${{ steps.channel.outputs.stability }}
|
||||
run: |
|
||||
DATE=$(date +%Y-%m-%d)
|
||||
|
||||
# Update version
|
||||
sed -i "s|<version>.*</version>|<version>${TAG}</version>|" updates.xml
|
||||
|
||||
# Update creation date
|
||||
sed -i "s|<creationDate>.*</creationDate>|<creationDate>${DATE}</creationDate>|" updates.xml
|
||||
|
||||
# Update download URL
|
||||
sed -i "s|<downloadurl type='full' format='zip'>.*</downloadurl>|<downloadurl type='full' format='zip'>https://github.com/${{ github.repository }}/releases/download/${TAG}/mokocassiopeia-src-${TAG}.zip</downloadurl>|" updates.xml
|
||||
|
||||
# Update or add SHA-256 hash
|
||||
if grep -q "<sha256>" updates.xml; then
|
||||
sed -i "s|<sha256>.*</sha256>|<sha256>sha256:${SHA256}</sha256>|" updates.xml
|
||||
else
|
||||
# Add SHA-256 after downloadurl
|
||||
sed -i "/<\/downloadurl>/a\ <sha256>sha256:${SHA256}<\/sha256>" updates.xml
|
||||
fi
|
||||
|
||||
echo "Updated updates.xml with:"
|
||||
echo " Version: ${TAG}"
|
||||
echo " Date: ${DATE}"
|
||||
echo " SHA-256: ${SHA256}"
|
||||
export PY_DATE="$DATE"
|
||||
|
||||
python3 << 'PYEOF'
|
||||
import re, os
|
||||
|
||||
tag = os.environ["PY_TAG"]
|
||||
sha256 = os.environ["PY_SHA"]
|
||||
date = os.environ["PY_DATE"]
|
||||
stability = os.environ["PY_STABILITY"]
|
||||
|
||||
with open("updates.xml") as f:
|
||||
content = f.read()
|
||||
|
||||
pattern = r"(<update>(?:(?!</update>).)*?<tag>" + re.escape(stability) + r"</tag>.*?</update>)"
|
||||
match = re.search(pattern, content, re.DOTALL)
|
||||
|
||||
if not match:
|
||||
print(f"No <update> block for <tag>{stability}</tag> — skipping")
|
||||
exit(0)
|
||||
|
||||
block = match.group(1)
|
||||
original = block
|
||||
|
||||
block = re.sub(r"<sha256>[^<]*</sha256>", f"<sha256>{sha256}</sha256>", block)
|
||||
block = re.sub(r"<creationDate>[^<]*</creationDate>", f"<creationDate>{date}</creationDate>", block)
|
||||
|
||||
content = content.replace(original, block)
|
||||
|
||||
with open("updates.xml", "w") as f:
|
||||
f.write(content)
|
||||
|
||||
print(f"Updated {stability} channel: sha={sha256[:16]}..., date={date}")
|
||||
PYEOF
|
||||
|
||||
- name: Check for changes
|
||||
id: changes
|
||||
@@ -118,8 +145,10 @@ jobs:
|
||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "gitea-actions[bot]"
|
||||
|
||||
STABILITY="${{ steps.channel.outputs.stability }}"
|
||||
git add updates.xml
|
||||
git commit -m "chore: Update SHA-256 hash for release ${TAG} - SHA: ${{ steps.sha.outputs.sha256 }}"
|
||||
git commit -m "chore: update ${STABILITY} SHA-256 for ${TAG} [skip ci]" \
|
||||
--author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>"
|
||||
|
||||
git push origin main
|
||||
|
||||
|
||||
98
.github/workflows/release.yml
vendored
98
.github/workflows/release.yml
vendored
@@ -95,6 +95,77 @@ jobs:
|
||||
echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT"
|
||||
echo "Building: ${ZIP_NAME} (${STABILITY})"
|
||||
|
||||
- name: Auto-bump patch version
|
||||
id: bump
|
||||
env:
|
||||
GA_TOKEN: ${{ secrets.GA_TOKEN }}
|
||||
INPUT_VERSION: ${{ steps.meta.outputs.version }}
|
||||
INPUT_STABILITY: ${{ steps.meta.outputs.stability }}
|
||||
INPUT_SUFFIX: ${{ steps.meta.outputs.suffix }}
|
||||
run: |
|
||||
# Read current version from README.md
|
||||
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)
|
||||
if [ -z "$CURRENT" ]; then
|
||||
echo "No VERSION in README.md — using input version"
|
||||
echo "version=${INPUT_VERSION}" >> "$GITHUB_OUTPUT"
|
||||
echo "zip_name=${EXT_ELEMENT}-${INPUT_VERSION}${INPUT_SUFFIX}.zip" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Bump patch: XX.YY.ZZ → XX.YY.(ZZ+1)
|
||||
MAJOR=$(echo "$CURRENT" | cut -d. -f1)
|
||||
MINOR=$(echo "$CURRENT" | cut -d. -f2)
|
||||
PATCH=$(echo "$CURRENT" | cut -d. -f3)
|
||||
NEW_PATCH=$(printf "%02d" $((10#$PATCH + 1)))
|
||||
NEW_VERSION="${MAJOR}.${MINOR}.${NEW_PATCH}"
|
||||
|
||||
echo "Bumping: ${CURRENT} → ${NEW_VERSION}"
|
||||
|
||||
# Update README.md
|
||||
sed -i "s/VERSION:[[:space:]]*${CURRENT}/VERSION: ${NEW_VERSION}/" README.md
|
||||
|
||||
# Update templateDetails.xml / manifest
|
||||
MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
|
||||
if [ -n "$MANIFEST" ]; then
|
||||
sed -i "s|<version>${CURRENT}</version>|<version>${NEW_VERSION}</version>|" "$MANIFEST"
|
||||
fi
|
||||
|
||||
# Update only the matching stability channel in updates.xml
|
||||
if [ -f "updates.xml" ]; then
|
||||
export PY_OLD="$CURRENT" PY_NEW="$NEW_VERSION" PY_STABILITY="$INPUT_STABILITY"
|
||||
python3 << 'PYEOF'
|
||||
import re, os
|
||||
old = os.environ["PY_OLD"]
|
||||
new = os.environ["PY_NEW"]
|
||||
stability = os.environ["PY_STABILITY"]
|
||||
with open("updates.xml") as f:
|
||||
content = f.read()
|
||||
pattern = r"(<update>(?:(?!</update>).)*?<tag>" + re.escape(stability) + r"</tag>.*?</update>)"
|
||||
match = re.search(pattern, content, re.DOTALL)
|
||||
if match:
|
||||
block = match.group(1)
|
||||
updated = block.replace(old, new)
|
||||
content = content.replace(block, updated)
|
||||
with open("updates.xml", "w") as f:
|
||||
f.write(content)
|
||||
print(f"Updated {stability} channel: {old} -> {new}")
|
||||
PYEOF
|
||||
fi
|
||||
|
||||
# Commit 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:${GA_TOKEN}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||
git add -A
|
||||
git diff --cached --quiet || {
|
||||
git commit -m "chore(version): bump ${CURRENT} → ${NEW_VERSION} [skip ci]" \
|
||||
--author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>"
|
||||
git push
|
||||
}
|
||||
|
||||
echo "version=${NEW_VERSION}" >> "$GITHUB_OUTPUT"
|
||||
echo "zip_name=${EXT_ELEMENT}-${NEW_VERSION}${INPUT_SUFFIX}.zip" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Install dependencies
|
||||
env:
|
||||
COMPOSER_AUTH: '{"http-basic":{"git.mokoconsulting.tech":{"username":"token","password":"${{ secrets.GA_TOKEN }}"}}}'
|
||||
@@ -119,7 +190,7 @@ jobs:
|
||||
- name: Build ZIP
|
||||
id: zip
|
||||
run: |
|
||||
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
||||
ZIP_NAME="${{ steps.bump.outputs.zip_name }}"
|
||||
cd build/package
|
||||
zip -r "../${ZIP_NAME}" .
|
||||
cd ..
|
||||
@@ -157,7 +228,7 @@ jobs:
|
||||
id: gitea_release
|
||||
run: |
|
||||
TAG="${{ steps.meta.outputs.tag_name }}"
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
VERSION="${{ steps.bump.outputs.version }}"
|
||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||
PRERELEASE="${{ steps.meta.outputs.prerelease }}"
|
||||
SHA256="${{ steps.zip.outputs.sha256 }}"
|
||||
@@ -207,7 +278,7 @@ jobs:
|
||||
- name: "Gitea: Upload ZIP"
|
||||
run: |
|
||||
RELEASE_ID="${{ steps.gitea_release.outputs.release_id }}"
|
||||
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
||||
ZIP_NAME="${{ steps.bump.outputs.zip_name }}"
|
||||
TOKEN="${{ secrets.GA_TOKEN }}"
|
||||
API="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
|
||||
@@ -225,9 +296,9 @@ jobs:
|
||||
continue-on-error: true
|
||||
run: |
|
||||
TAG="${{ steps.meta.outputs.tag_name }}"
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
VERSION="${{ steps.bump.outputs.version }}"
|
||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
||||
ZIP_NAME="${{ steps.bump.outputs.zip_name }}"
|
||||
SHA256="${{ steps.zip.outputs.sha256 }}"
|
||||
TOKEN="${{ secrets.GH_TOKEN }}"
|
||||
GH_REPO="mokoconsulting-tech/${GITEA_REPO}"
|
||||
@@ -275,9 +346,9 @@ jobs:
|
||||
- name: "Update updates.xml for this channel"
|
||||
run: |
|
||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
VERSION="${{ steps.bump.outputs.version }}"
|
||||
SHA256="${{ steps.zip.outputs.sha256 }}"
|
||||
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
||||
ZIP_NAME="${{ steps.bump.outputs.zip_name }}"
|
||||
TAG="${{ steps.meta.outputs.tag_name }}"
|
||||
DATE=$(date +%Y-%m-%d)
|
||||
|
||||
@@ -332,8 +403,11 @@ jobs:
|
||||
# Update creation date
|
||||
block = re.sub(r"<creationDate>[^<]*</creationDate>", f"<creationDate>{date}</creationDate>", block)
|
||||
|
||||
# Update SHA-256
|
||||
block = re.sub(r"<sha256>[^<]*</sha256>", f"<sha256>{sha256}</sha256>", block)
|
||||
# Update or add SHA-256
|
||||
if "<sha256>" in block:
|
||||
block = re.sub(r"<sha256>[^<]*</sha256>", f"<sha256>{sha256}</sha256>", block)
|
||||
else:
|
||||
block = block.replace("</downloads>", f"</downloads>\n <sha256>{sha256}</sha256>")
|
||||
|
||||
# Update Gitea download URL
|
||||
gitea_url = f"https://git.mokoconsulting.tech/{gitea_org}/{gitea_repo}/releases/download/{tag}/{zip_name}"
|
||||
@@ -375,7 +449,7 @@ jobs:
|
||||
fi
|
||||
|
||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
VERSION="${{ steps.bump.outputs.version }}"
|
||||
CURRENT_BRANCH="${{ github.ref_name }}"
|
||||
TOKEN="${{ secrets.GA_TOKEN }}"
|
||||
|
||||
@@ -428,9 +502,9 @@ jobs:
|
||||
|
||||
- name: Summary
|
||||
run: |
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
VERSION="${{ steps.bump.outputs.version }}"
|
||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
||||
ZIP_NAME="${{ steps.bump.outputs.zip_name }}"
|
||||
SHA256="${{ steps.zip.outputs.sha256 }}"
|
||||
TAG="${{ steps.meta.outputs.tag_name }}"
|
||||
|
||||
|
||||
@@ -9,15 +9,15 @@
|
||||
INGROUP: MokoCassiopeia.Documentation
|
||||
REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia
|
||||
FILE: ./README.md
|
||||
VERSION: 03.10.10
|
||||
VERSION: 03.10.21
|
||||
BRIEF: Documentation for MokoCassiopeia template
|
||||
-->
|
||||
|
||||
# MokoCassiopeia → MokoOnyx
|
||||
# MokoCassiopeia (Retired)
|
||||
|
||||
> **This template is being renamed to MokoOnyx.** Version 03.10.10 is the bridge release that automatically migrates your settings. After updating, MokoOnyx will be your active template and MokoCassiopeia can be safely uninstalled.
|
||||
> **This template has been retired and replaced by [MokoOnyx](https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx).** MokoCassiopeia is no longer maintained. To migrate, install MokoOnyx from [Gitea Releases](https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/v01), set it as your default template, and visit any page — your settings will be imported automatically. Then uninstall MokoCassiopeia.
|
||||
|
||||
**A Modern, Lightweight Joomla Template Based on Cassiopeia**
|
||||
**Retired — See [MokoOnyx](https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx)**
|
||||
|
||||
[](https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/tag/v03)
|
||||
[](LICENSE)
|
||||
|
||||
926
docs/ROADMAP.md
926
docs/ROADMAP.md
@@ -1,917 +1,31 @@
|
||||
<!--
|
||||
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
|
||||
This file is part of a Moko Consulting project.
|
||||
This file is part of a Moko Consulting project.
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
# FILE INFORMATION
|
||||
DEFGROUP: Joomla.Template.Site
|
||||
INGROUP: MokoCassiopeia.Documentation
|
||||
REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-cassiopeia
|
||||
FILE: docs/ROADMAP.md
|
||||
VERSION: 03.09.03
|
||||
BRIEF: Version-specific roadmap for MokoCassiopeia template
|
||||
PATH: /docs/ROADMAP.md
|
||||
# FILE INFORMATION
|
||||
DEFGROUP: Joomla.Template.Site
|
||||
INGROUP: MokoCassiopeia.Documentation
|
||||
REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia
|
||||
PATH: /docs/ROADMAP.md
|
||||
VERSION: 03.10.20
|
||||
BRIEF: Redirect to MokoOnyx roadmap
|
||||
-->
|
||||
|
||||
# MokoCassiopeia Roadmap (VERSION: 03.09.03)
|
||||
# MokoCassiopeia Roadmap
|
||||
|
||||
This document provides a comprehensive, version-specific roadmap for the MokoCassiopeia Joomla template, tracking feature evolution, current capabilities, and planned enhancements.
|
||||
**MokoCassiopeia has been renamed to MokoOnyx.** All future development continues under the MokoOnyx project.
|
||||
|
||||
## Table of Contents
|
||||
See the [MokoOnyx Roadmap](https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/src/branch/dev/docs/ROADMAP.md) for all planned features and releases.
|
||||
|
||||
- [Version Timeline](#version-timeline)
|
||||
- [Past Releases](#past-releases)
|
||||
- [Future Roadmap (5-Year Plan)](#future-roadmap-5-year-plan)
|
||||
- [Current Release (v03.06.03)](#current-release-v030603)
|
||||
- [Implemented Features](#implemented-features)
|
||||
- [Planned Features](#planned-features)
|
||||
- [Development Priorities](#development-priorities)
|
||||
- [Long-term Vision](#long-term-vision)
|
||||
- [External Resources](#external-resources)
|
||||
## Migration
|
||||
|
||||
---
|
||||
To migrate from MokoCassiopeia to MokoOnyx:
|
||||
|
||||
## Version Timeline
|
||||
|
||||
### Past Releases
|
||||
|
||||
### v03.05.01 (2026-01-09) - Standards & Security
|
||||
**Status**: Released (CHANGELOG entry exists, code files pending version update)
|
||||
|
||||
**Added**:
|
||||
- Dependency review workflow for vulnerability scanning
|
||||
- Standards compliance workflow for MokoStandards validation
|
||||
- Dependabot configuration for automated security updates
|
||||
- Documentation index (`docs/README.md`)
|
||||
|
||||
**Changed**:
|
||||
- Removed custom CodeQL workflow (using GitHub's default setup)
|
||||
- Enforced repository compliance with MokoStandards
|
||||
- Improved security posture with automated scanning
|
||||
|
||||
### v03.06.00 (2026-01-28) - Version Update
|
||||
**Status**: Current Release (in code)
|
||||
|
||||
**Changed**:
|
||||
- Updated version to 03.06.00 across all files
|
||||
|
||||
### v03.05.00 (2026-01-04) - Workflow & Governance
|
||||
**Status**: Mentioned in CHANGELOG (v03.05.00)
|
||||
|
||||
**Added**:
|
||||
- `.github/workflows` directory structure
|
||||
- CODE_OF_CONDUCT.md from MokoStandards
|
||||
- CONTRIBUTING.md from MokoStandards
|
||||
|
||||
**Changed**:
|
||||
- TODO items to be split to separate file (tracked)
|
||||
|
||||
### v03.01.00 (2025-12-16) - CI/CD Foundation
|
||||
**Added**:
|
||||
- Initial GitHub Actions workflows
|
||||
|
||||
### v03.00.00 (2025-12-09) - Font Awesome 7 Upgrade
|
||||
**Updated**:
|
||||
- Copyright headers to MokoCodingDefaults standards
|
||||
- Fixed color style injection in `index.php`
|
||||
- Upgraded Font Awesome 6 to Font Awesome 7 Free
|
||||
- Added Font Awesome 7 Free style fallback
|
||||
|
||||
**Removed**:
|
||||
- Deprecated CODE_OF_CONDUCT.md
|
||||
- Deprecated CONTRIBUTING.md
|
||||
|
||||
### v02.01.05 (2025-09-04) - CSS Refinement
|
||||
**Fixed**:
|
||||
- Removed vmbasic.css
|
||||
- Repaired template.css and colors_standard.css
|
||||
|
||||
### v02.00.00 (2025-08-30) - Dark Mode & TOC
|
||||
**Major Features**:
|
||||
- **Dark Mode Toggle System**
|
||||
- Frontend toggle switch with localStorage persistence
|
||||
- Admin-configurable default mode
|
||||
- CSS rules for light/dark themes
|
||||
- JavaScript-powered mode switching
|
||||
|
||||
- **Enhanced Template Parameters**
|
||||
- Logo parameter support
|
||||
- GTM container ID configuration
|
||||
- Dark mode defaults in settings
|
||||
- Updated metadata and copyright headers
|
||||
|
||||
- **Expanded Table of Contents**
|
||||
- Automatic TOC injection
|
||||
- User-selectable placement (`toc-left` or `toc-right`)
|
||||
- Article options integration
|
||||
|
||||
**Improvements**:
|
||||
- Cleaned up `index.php` (removed duplicate skip-to-content calls)
|
||||
- Consolidated JavaScript asset loading
|
||||
- Streamlined CSS for toggle switch
|
||||
- Accessibility refinements (typography, color contrast)
|
||||
- Fixed missing logo parameter in header
|
||||
- Corrected stylesheet inconsistencies
|
||||
- Patched redundant script includes
|
||||
|
||||
### v01.00.00 - Initial Public Release
|
||||
**Core Features**:
|
||||
- Font Awesome 6 integration
|
||||
- Bootstrap 5 helpers and utilities
|
||||
- Automatic Table of Contents (TOC) utility
|
||||
- Moko Expansions: Google Tag Manager / GA4 hooks
|
||||
- Built on Joomla's Cassiopeia template
|
||||
|
||||
---
|
||||
|
||||
### Future Roadmap (5-Year Plan)
|
||||
|
||||
The following versions represent our planned annual major releases, each building upon the previous version's foundation.
|
||||
|
||||
#### v04.00.00 (Q4 2027) - Enhanced Accessibility & Performance
|
||||
**Status**: Planned
|
||||
**Target Release**: December 2027
|
||||
|
||||
**Major Template Features**:
|
||||
- **WCAG 2.1 AA Compliance**
|
||||
- Full accessibility audit and remediation
|
||||
- High-contrast theme options
|
||||
- Screen reader optimizations
|
||||
- Keyboard navigation enhancements
|
||||
- ARIA landmark improvements
|
||||
- Skip navigation enhancements
|
||||
|
||||
- **Template Performance Optimizations**
|
||||
- Critical CSS inlining for faster first paint
|
||||
- Lazy loading for images and below-fold content
|
||||
- WebP image support with automatic fallbacks
|
||||
- Advanced asset bundling and minification
|
||||
- Template asset caching (CSS/JS bundles)
|
||||
|
||||
- **Enhanced Layout System**
|
||||
- Additional responsive grid layouts
|
||||
- Flexible module position system
|
||||
- Column layout presets (2-col, 3-col, 4-col variations)
|
||||
- Grid/masonry article layouts
|
||||
- Sticky sidebar options
|
||||
|
||||
- **Typography Enhancements**
|
||||
- Advanced typography controls in template settings
|
||||
- Additional font pairing presets
|
||||
- Custom font upload support
|
||||
- Line height and letter spacing controls
|
||||
- Responsive typography scaling
|
||||
|
||||
- **Developer Experience**
|
||||
- Development mode enablement (unminified assets, debug output)
|
||||
- Live reload during development
|
||||
- Enhanced error logging and diagnostics
|
||||
- Template debugging tools
|
||||
- Style guide generator
|
||||
|
||||
- **Content Display Features**
|
||||
- Soft offline mode (category-based access during maintenance)
|
||||
- Enhanced article layouts (grid, masonry, timeline)
|
||||
- Image caption styling options
|
||||
- Quote block styling variations
|
||||
- Enhanced breadcrumb customization
|
||||
|
||||
**Template Infrastructure**:
|
||||
- Expanded template parameter validation
|
||||
- Enhanced template override detection
|
||||
- Automated template compatibility testing
|
||||
- Template performance profiling tools
|
||||
|
||||
---
|
||||
|
||||
#### v05.00.00 (Q4 2028) - Advanced Layouts & Template Customization
|
||||
**Status**: Planned
|
||||
**Target Release**: December 2028
|
||||
|
||||
**Major Template Features**:
|
||||
- **Enhanced Layout Builder**
|
||||
- Template-based page layout variations
|
||||
- Configurable layout options via template parameters
|
||||
- Layout presets library (blog, portfolio, business, magazine)
|
||||
- Module position layout manager
|
||||
- Visual layout preview in admin
|
||||
|
||||
- **Advanced Styling System**
|
||||
- Extended color palette management (unlimited custom palettes)
|
||||
- CSS variable editor in template settings
|
||||
- Style presets for different site types
|
||||
- Border radius and spacing controls
|
||||
- Box shadow and effect controls
|
||||
|
||||
- **Template Component Enhancements**
|
||||
- Enhanced menu styling options (mega menu support)
|
||||
- Advanced header variations (transparent, sticky, minimal)
|
||||
- Footer layout options (column variations, widgets)
|
||||
- Sidebar styling and behavior options
|
||||
- Hero section templates and variations
|
||||
|
||||
- **Content Display Options**
|
||||
- Article intro/full text display controls
|
||||
- Category layout variations (grid, list, masonry, cards)
|
||||
- Featured content sections
|
||||
- Related articles display options
|
||||
- Author bio box styling
|
||||
|
||||
- **Responsive Design Improvements**
|
||||
- Mobile-first navigation patterns
|
||||
- Tablet-specific layout controls
|
||||
- Responsive image sizing options
|
||||
- Mobile header variations
|
||||
- Touch-friendly interface elements
|
||||
|
||||
- **Template Integration Features**
|
||||
- Enhanced VirtueMart template overrides
|
||||
- Contact form styling variations
|
||||
- Search result layout options
|
||||
- Error page customization
|
||||
- Archive page templates
|
||||
|
||||
**Template Infrastructure**:
|
||||
- Joomla 6.x template compatibility (if released)
|
||||
- PHP 8.2+ support
|
||||
- Template child theme support
|
||||
- Template preset import/export functionality
|
||||
|
||||
---
|
||||
|
||||
#### v06.00.00 (Q4 2029) - Template Extensions & Advanced Features
|
||||
**Status**: Planned
|
||||
**Target Release**: December 2029
|
||||
|
||||
**Major Template Features**:
|
||||
- **Template Marketplace & Extensions**
|
||||
- Template addon system for modular features
|
||||
- Community-contributed template extensions
|
||||
- Template preset marketplace
|
||||
- Style pack distribution system
|
||||
- Template component library
|
||||
|
||||
- **Advanced Module System**
|
||||
- Custom module chrome options
|
||||
- Module animation effects
|
||||
- Module visibility controls (scroll, time-based)
|
||||
- Module group management
|
||||
- Module style inheritance
|
||||
|
||||
- **Enhanced Media Handling**
|
||||
- Background image options per page/section
|
||||
- Image overlay controls
|
||||
- Parallax scrolling effects
|
||||
- Video background support
|
||||
- Gallery template variations
|
||||
|
||||
- **Template Branding Options**
|
||||
- Multiple logo upload (standard, retina, mobile)
|
||||
- Favicon and app icon management
|
||||
- Custom loading screen/animations
|
||||
- Watermark options
|
||||
- Brand color scheme generator
|
||||
|
||||
- **Advanced Header/Footer**
|
||||
- Multiple header layout presets
|
||||
- Sticky header variations and behaviors
|
||||
- Header transparency controls
|
||||
- Footer widget areas expansion
|
||||
- Floating action buttons
|
||||
|
||||
- **Content Enhancement Features**
|
||||
- Reading progress indicator
|
||||
- Social sharing buttons (template-integrated)
|
||||
- Print-friendly styles
|
||||
- Reading time estimation display
|
||||
- Content table enhancements
|
||||
|
||||
- **Template SEO Features**
|
||||
- Schema markup templates for common types
|
||||
- Open Graph tag management
|
||||
- Twitter Card support
|
||||
- Breadcrumb schema integration
|
||||
- Meta tag template controls
|
||||
|
||||
**Template Infrastructure**:
|
||||
- Template versioning system
|
||||
- Template backup/restore functionality
|
||||
- Template A/B testing support
|
||||
- Multi-language template variations
|
||||
- Template documentation generator
|
||||
|
||||
---
|
||||
|
||||
#### v07.00.00 (Q4 2030) - Modern Template Standards & Enhancements
|
||||
**Status**: Planned
|
||||
**Target Release**: December 2030
|
||||
|
||||
**Major Template Features**:
|
||||
- **Modern CSS Features**
|
||||
- CSS Grid layout system integration
|
||||
- CSS Container Queries support
|
||||
- CSS Cascade Layers implementation (layered style priority system)
|
||||
- Custom properties (CSS variables) UI
|
||||
- Modern filter and backdrop effects
|
||||
|
||||
- **Progressive Template Features**
|
||||
- Offline-capable template assets
|
||||
- Service worker template integration
|
||||
- App manifest generation
|
||||
- Install to home screen support
|
||||
- Template asset preloading strategies
|
||||
|
||||
- **Animation & Interaction**
|
||||
- Scroll-triggered animations
|
||||
- Hover effect library
|
||||
- Page transition effects
|
||||
- Micro-interactions for UI elements
|
||||
- Loading animation options
|
||||
|
||||
- **Advanced Responsive Features**
|
||||
- Container-based responsive design
|
||||
- Element visibility by viewport
|
||||
- Responsive navigation patterns library
|
||||
- Mobile-optimized interactions
|
||||
- Adaptive image loading
|
||||
|
||||
- **Template Accessibility Features**
|
||||
- Focus indicators customization
|
||||
- Reduced motion preferences support
|
||||
- High contrast mode automation
|
||||
- Keyboard navigation patterns
|
||||
- ARIA live regions for dynamic content
|
||||
|
||||
- **Content Presentation**
|
||||
- Advanced blockquote styles
|
||||
- Code snippet highlighting themes
|
||||
- Table styling variations
|
||||
- List styling options
|
||||
- Custom content block templates
|
||||
|
||||
- **Template Performance**
|
||||
- Resource hints (preconnect, prefetch)
|
||||
- Optimal asset delivery strategies
|
||||
- Image format optimization (AVIF support)
|
||||
- Font loading optimization
|
||||
- Template metrics dashboard
|
||||
|
||||
**Template Infrastructure**:
|
||||
- Template pattern library
|
||||
- Design token system
|
||||
- Template component documentation
|
||||
- Automated template testing suite
|
||||
- Template performance monitoring
|
||||
|
||||
---
|
||||
|
||||
#### v08.00.00 (Q4 2031) - Next-Generation Template Features
|
||||
**Status**: Conceptual
|
||||
**Target Release**: December 2031
|
||||
|
||||
**Major Template Features**:
|
||||
- **Advanced Layout Systems**
|
||||
- Subgrid support for complex layouts
|
||||
- Multi-column layout variations
|
||||
- Asymmetric grid systems
|
||||
- Dynamic layout switching
|
||||
- Layout constraint system
|
||||
|
||||
- **Enhanced Visual Customization**
|
||||
- Real-time style editor
|
||||
- Template style variations manager
|
||||
- Custom CSS injection with validation
|
||||
- Style inheritance and override system
|
||||
- Visual design tokens editor
|
||||
|
||||
- **Template Component Library**
|
||||
- Comprehensive UI component set
|
||||
- Reusable template blocks
|
||||
- Component variation system
|
||||
- Template snippet library
|
||||
- Pattern library integration
|
||||
|
||||
- **Advanced Typography System**
|
||||
- Variable font support
|
||||
- Advanced typographic scales
|
||||
- Font pairing recommendations
|
||||
- Fluid typography system
|
||||
- Custom font fallback chains
|
||||
|
||||
- **Template Integration Features**
|
||||
- Enhanced component overrides
|
||||
- Template hooks system
|
||||
- Event-based template modifications
|
||||
- Custom field rendering templates
|
||||
- Module position API enhancements
|
||||
|
||||
- **Responsive & Adaptive Design**
|
||||
- Advanced breakpoint management
|
||||
- Element-specific responsive controls
|
||||
- Adaptive images with art direction
|
||||
- Responsive typography system
|
||||
- Context-aware component rendering
|
||||
|
||||
- **Template Ecosystem**
|
||||
- Child template framework
|
||||
- Template derivative system
|
||||
- Community template marketplace
|
||||
- Template rating and review system
|
||||
- Professional template support network
|
||||
|
||||
- **Template Quality & Maintenance**
|
||||
- Automated accessibility testing
|
||||
- Template performance auditing
|
||||
- Code quality monitoring
|
||||
- Update notification system
|
||||
- Template health dashboard
|
||||
|
||||
**Template Infrastructure**:
|
||||
- Template API for extensibility
|
||||
- Template package manager
|
||||
- Template development CLI tools
|
||||
- Template migration utilities
|
||||
- Comprehensive template documentation system
|
||||
|
||||
---
|
||||
|
||||
## Current Release (v03.06.03)
|
||||
|
||||
### System Requirements
|
||||
- **Joomla**: 4.4.x or 5.x
|
||||
- **PHP**: 8.0+
|
||||
- **Database**: MySQL/MariaDB compatible
|
||||
|
||||
### Architecture
|
||||
- **Base Template**: Joomla Cassiopeia
|
||||
- **Enhancement Layer**: Non-invasive overrides
|
||||
- **Asset Management**: Joomla Web Asset Manager (WAM)
|
||||
- **Frontend Framework**: Bootstrap 5
|
||||
- **Icon Library**: Font Awesome 7 Free
|
||||
|
||||
---
|
||||
|
||||
## Implemented Features
|
||||
|
||||
### 🎨 Theming & Visual Design
|
||||
|
||||
#### Color Palette System
|
||||
- **3 Built-in Palettes**: Standard, Alternative, Custom
|
||||
- **Dual Mode Support**: Separate light and dark configurations
|
||||
- **Custom Palettes**: User-definable via `colors_custom.css`
|
||||
- **Location**: `src/media/css/colors/{light|dark}/`
|
||||
|
||||
#### Dark Mode System
|
||||
- **Toggle Controls**: Switch (Light↔Dark) or Radios (Light/Dark/System)
|
||||
- **Default Mode**: Admin-configurable (system, light, or dark)
|
||||
- **Persistence**: localStorage for user preferences
|
||||
- **Auto-Detection**: Optional system preference detection
|
||||
- **Meta Tags**: `color-scheme` and `theme-color` support
|
||||
- **ARIA Bridge**: Bootstrap ARIA compatibility
|
||||
|
||||
#### Typography
|
||||
- **Font Schemes**:
|
||||
- Local: Roboto
|
||||
- Web (Google Fonts): Fira Sans, Roboto + Noto Sans
|
||||
- **Admin-Configurable**: Template settings dropdown
|
||||
|
||||
#### Branding
|
||||
- **Logo Support**: Custom logo upload
|
||||
- **Site Title**: Text-based branding option
|
||||
- **Site Description**: Tagline/subtitle field
|
||||
- **Font Awesome Kit**: Optional custom kit integration
|
||||
|
||||
### 📐 Layout & Structure
|
||||
|
||||
#### Module Positions (23 Total)
|
||||
**Header Area**:
|
||||
- topbar, below-topbar, below-logo, menu, search, banner
|
||||
|
||||
**Content Area**:
|
||||
- top-a, top-b, main-top, main-bottom, breadcrumbs
|
||||
- sidebar-left, sidebar-right
|
||||
|
||||
**Footer Area**:
|
||||
- bottom-a, bottom-b, footer-menu, footer
|
||||
|
||||
**Special**:
|
||||
- debug, offline-header, offline, offline-footer
|
||||
- drawer-left, drawer-right
|
||||
|
||||
#### Layout Options
|
||||
- **Container Type**: Fluid or Static
|
||||
- **Sticky Header**: Optional fixed navigation
|
||||
- **Back-to-Top Button**: Scrollable page support
|
||||
|
||||
### 📝 Content Features
|
||||
|
||||
#### Table of Contents (TOC)
|
||||
- **Automatic Generation**: From article headings
|
||||
- **Placement Options**: `toc-left` or `toc-right` layouts
|
||||
- **Article Integration**: Via Options → Layout dropdown
|
||||
- **Responsive**: Mobile-friendly sidebar placement
|
||||
|
||||
#### Article Layouts
|
||||
- **Default**: Standard Cassiopeia layout
|
||||
- **TOC Variants**: Left-sidebar or right-sidebar TOC
|
||||
- **Custom Overrides**: Located in `html/com_content/article/`
|
||||
|
||||
### 📊 Analytics & Tracking
|
||||
|
||||
#### Google Tag Manager (GTM)
|
||||
- **Enable/Disable**: Admin toggle
|
||||
- **Container ID**: Template parameter field
|
||||
- **Implementation**: Head and body script injection
|
||||
- **GDPR-Ready**: Configurable consent defaults
|
||||
|
||||
#### Google Analytics 4 (GA4)
|
||||
- **Enable/Disable**: Admin toggle
|
||||
- **Property ID**: Template parameter field
|
||||
- **Universal Analytics Fallback**: Legacy UA support
|
||||
- **Privacy-First**: Conditional loading based on settings
|
||||
|
||||
### 🎛️ Customization & Developer Tools
|
||||
|
||||
#### Custom Code Injection
|
||||
- **Head Start**: Custom HTML/JS before `</head>`
|
||||
- **Head End**: Custom HTML/JS at end of `<head>`
|
||||
- **Raw HTML**: Unfiltered code injection for advanced users
|
||||
|
||||
#### Drawer System
|
||||
- **Left/Right Drawers**: Offcanvas menu areas
|
||||
- **Icon Customization**: Font Awesome icon selection
|
||||
- **Default Icons**:
|
||||
- Left: `fa-solid fa-chevron-right`
|
||||
- Right: `fa-solid fa-chevron-left`
|
||||
|
||||
#### Asset Management
|
||||
- **Joomla WAM**: Complete asset registry in `joomla.asset.json`
|
||||
- **Development/Production Modes**: Minified and unminified assets
|
||||
- **Dependency Management**: Automatic script/style loading
|
||||
|
||||
### 🏗️ Template Overrides
|
||||
|
||||
#### Component Overrides
|
||||
**Content (com_content)**:
|
||||
- Article layouts (default, toc-left, toc-right)
|
||||
- Category layouts (blog, list)
|
||||
- Featured articles
|
||||
|
||||
**Contact (com_contact)**:
|
||||
- Contact form layouts
|
||||
|
||||
**Engage (com_engage)**:
|
||||
- Comment system integration
|
||||
|
||||
#### Module Overrides
|
||||
**Menu (mod_menu)**:
|
||||
- Metis dropdown menu
|
||||
- Offcanvas navigation
|
||||
|
||||
**VirtueMart**:
|
||||
- Product display (`mod_virtuemart_product`)
|
||||
- Shopping cart (`mod_virtuemart_cart`)
|
||||
- Manufacturer display (`mod_virtuemart_manufacturer`)
|
||||
- Category display (`mod_virtuemart_category`)
|
||||
- Currency selector (`mod_virtuemart_currencies`)
|
||||
|
||||
**Other Modules**:
|
||||
- Custom HTML (`mod_custom`)
|
||||
- GABble social integration (`mod_gabble`)
|
||||
|
||||
**Membership System (OS Membership)**:
|
||||
- Plan layouts (default, pricing tables)
|
||||
- Member management interfaces
|
||||
|
||||
### 🔧 Configuration Parameters
|
||||
|
||||
#### Theme Tab
|
||||
**General**:
|
||||
- `theme_enabled` - Enable/disable theme system
|
||||
- `theme_control_type` - Toggle UI type (switch/radios/none)
|
||||
- `theme_default_choice` - Default mode (system/light/dark)
|
||||
- `theme_auto_dark` - Auto-detect system preference
|
||||
- `theme_meta_color_scheme` - Inject `color-scheme` meta tag
|
||||
- `theme_meta_theme_color` - Inject `theme-color` meta tag
|
||||
- `theme_bridge_bs_aria` - Bootstrap ARIA compatibility
|
||||
|
||||
**Variables & Palettes**:
|
||||
- `colorLightName` - Light mode color scheme
|
||||
- `colorDarkName` - Dark mode color scheme
|
||||
|
||||
**Typography**:
|
||||
- `useFontScheme` - Font selection (local/web)
|
||||
|
||||
**Branding & Icons**:
|
||||
- `brand` - Show/hide branding
|
||||
- `logoFile` - Logo upload path
|
||||
- `siteTitle` - Site title text
|
||||
- `siteDescription` - Site tagline
|
||||
- `fA6KitCode` - Font Awesome kit code
|
||||
|
||||
**Header & Navigation**:
|
||||
- `stickyHeader` - Fixed navigation
|
||||
- `backTop` - Back-to-top button
|
||||
|
||||
**Toggle UI**:
|
||||
- `theme_fab_enabled` - Floating action button for theme toggle
|
||||
- `theme_fab_pos` - FAB position (br/bl/tr/tl)
|
||||
|
||||
#### Google Tab
|
||||
- `googletagmanager` - Enable GTM
|
||||
- `googletagmanagerid` - GTM container ID
|
||||
- `googleanalytics` - Enable GA4
|
||||
- `googleanalyticsid` - GA4 property ID
|
||||
|
||||
#### Custom Code Tab
|
||||
- `custom_head_start` - Custom code at head start
|
||||
- `custom_head_end` - Custom code at head end
|
||||
|
||||
#### Drawers Tab
|
||||
- `drawerLeftIcon` - Left drawer icon (Font Awesome class)
|
||||
- `drawerRightIcon` - Right drawer icon (Font Awesome class)
|
||||
|
||||
#### Advanced Tab
|
||||
- `fluidContainer` - Container layout (static/fluid)
|
||||
|
||||
### 🛠️ Development Tools
|
||||
|
||||
#### Quality Assurance
|
||||
- **Codeception**: Automated testing framework
|
||||
- **PHPStan**: Static analysis (level 8+)
|
||||
- **PHPCS**: Code style validation (PSR-12)
|
||||
- **PHPCompatibility**: PHP 8.0+ compatibility checks
|
||||
|
||||
#### CI/CD Workflows
|
||||
- **Dependency Review**: Vulnerability scanning
|
||||
- **Standards Compliance**: MokoStandards validation
|
||||
- **CodeQL**: Security analysis (GitHub default)
|
||||
- **Dependabot**: Automated dependency updates
|
||||
|
||||
#### Documentation
|
||||
- **Quick Start**: 5-minute developer setup
|
||||
- **Workflow Guide**: Git strategy, branching, releases
|
||||
- **Joomla Development**: Testing, packaging, multi-version support
|
||||
|
||||
---
|
||||
|
||||
## Planned Features
|
||||
|
||||
### 🚧 In Development
|
||||
|
||||
#### Soft Offline Mode (v03.07.00 - Planned)
|
||||
**Status**: Planned for v03.07.00
|
||||
**Priority**: High
|
||||
**Description**: Keep selected categories accessible during site maintenance mode with persistent links to essential pages
|
||||
|
||||
**Use Cases**:
|
||||
- Legal documents remain viewable during downtime
|
||||
- Policy pages accessible for compliance requirements
|
||||
- Terms of service always available to users
|
||||
- Privacy policy accessible at all times
|
||||
- Essential public information during maintenance
|
||||
|
||||
**Technical Specifications**:
|
||||
- **Configuration Method**: Template parameters in `templateDetails.xml`
|
||||
- **Category Access**: Category IDs stored as comma-separated values
|
||||
- **Persistent Links**: Direct article/menu item links always visible
|
||||
- **Access Control**: Check in `offline.php` template file
|
||||
- **Content Rendering**: Use Joomla's content component to fetch articles
|
||||
- **Security**: Maintain proper access levels and permissions
|
||||
|
||||
**Implementation Plan**:
|
||||
1. Add category selection field to template parameters
|
||||
2. Add persistent link configuration (Terms of Service, Privacy Policy, etc.)
|
||||
3. Modify `offline.php` to check for allowed categories
|
||||
4. Add persistent link display in offline mode header/footer
|
||||
5. Implement category content fetching during offline mode
|
||||
6. Add styling for offline mode category display and persistent links
|
||||
7. Test with various category and link configurations
|
||||
8. Document admin configuration steps
|
||||
|
||||
**Configuration Interface**:
|
||||
- **Category Field Type**: Category multiselect in template settings
|
||||
- **Label**: "Categories Accessible During Offline Mode"
|
||||
- **Default**: None (all content hidden by default)
|
||||
- **Persistent Links**: Text fields for essential always-available links
|
||||
- **Terms of Service URL**: Direct link to TOS article/page
|
||||
- **Privacy Policy URL**: Direct link to privacy policy
|
||||
- **Contact URL**: Optional contact page link
|
||||
- **Custom Link 1-3**: Additional persistent links if needed
|
||||
- **Admin Path**: System → Site Templates → MokoCassiopeia → Advanced → Offline Mode Settings
|
||||
|
||||
**Persistent Links Feature**:
|
||||
- **Display Location**: Footer of offline page
|
||||
- **Styling**: Clearly visible, accessible links
|
||||
- **Format**: "Terms of Service | Privacy Policy | Contact"
|
||||
- **Behavior**: Links bypass offline mode restrictions
|
||||
- **Validation**: Check if URLs are valid Joomla routes
|
||||
|
||||
**Benefits**:
|
||||
- ✅ Compliance: Keep legal pages accessible
|
||||
- ✅ Transparency: Users can access essential information
|
||||
- ✅ Flexibility: Admin control over which categories remain visible
|
||||
- ✅ Security: Respects Joomla access levels
|
||||
- ✅ Legal Protection: Terms of Service always accessible
|
||||
- ✅ User Trust: Privacy policy always available
|
||||
|
||||
**Milestone**: Target release v03.07.00 (Q2 2026)
|
||||
|
||||
#### TODO Tracking System
|
||||
**Status**: Mentioned in CHANGELOG (v03.05.00)
|
||||
**Description**: Separate TODO tracking file
|
||||
**Purpose**: Centralized issue and feature tracking outside changelog
|
||||
|
||||
### 🔮 Future Enhancements
|
||||
|
||||
#### Development Mode (Commented Out)
|
||||
**Status**: Code exists but disabled
|
||||
**Location**: `templateDetails.xml` line 91
|
||||
**Description**: Comprehensive development mode toggle
|
||||
**Potential Features**:
|
||||
- Unminified asset loading
|
||||
- Debug output
|
||||
- Performance profiling
|
||||
- Template cache bypass
|
||||
|
||||
#### Potential Features (Community Requested)
|
||||
*Note: These are conceptual and not yet officially planned*
|
||||
|
||||
**Enhanced Accessibility**:
|
||||
- WCAG 2.1 AAA compliance mode
|
||||
- High-contrast themes
|
||||
- Screen reader optimizations
|
||||
- Keyboard navigation improvements
|
||||
|
||||
**Template Layout Features**:
|
||||
- Advanced responsive grid layouts
|
||||
- Multiple column variations
|
||||
- Custom module position system
|
||||
- Layout preset library
|
||||
|
||||
**Template Styling Features**:
|
||||
- Extended color palette management
|
||||
- Custom font upload support
|
||||
- Typography scale controls
|
||||
- Visual style editor
|
||||
|
||||
---
|
||||
|
||||
## Development Priorities
|
||||
|
||||
### Immediate Focus (v03.x - 2026)
|
||||
1. **Bootstrap TOC Integration**: Complete and document v1.0.1 implementation ✅
|
||||
2. **Soft Offline Mode**: Implement category-based offline access (Target: v03.07.00)
|
||||
3. **TODO Tracking System**: Implement separate file for issue tracking
|
||||
4. **Security Updates**: Maintain Dependabot and CodeQL scans
|
||||
5. **Documentation**: Keep docs synchronized with features
|
||||
6. **Bug Fixes**: Address reported issues and edge cases
|
||||
|
||||
### v04.00.00 Priorities (2027) - Template Foundation
|
||||
1. **WCAG 2.1 AA Compliance**: Full template accessibility audit and implementation
|
||||
2. **Template Performance**: Critical CSS, lazy loading, WebP support
|
||||
3. **Layout System**: Enhanced responsive grid and module positions
|
||||
4. **Development Mode**: Enable comprehensive template developer tools
|
||||
|
||||
### v05.00.00 Priorities (2028) - Template Customization
|
||||
1. **Layout Builder**: Template-based page layout system
|
||||
2. **Styling System**: Extended color palettes and CSS variable management
|
||||
3. **Template Components**: Enhanced header, footer, and menu variations
|
||||
4. **Responsive Design**: Mobile-first navigation and layout improvements
|
||||
|
||||
### v06.00.00 Priorities (2029) - Template Extensions
|
||||
1. **Template Marketplace**: Addon system and community extensions
|
||||
2. **Module System**: Advanced module chrome and animation options
|
||||
3. **Media Handling**: Background images, parallax, video backgrounds
|
||||
4. **Template SEO**: Schema markup templates and meta tag controls
|
||||
|
||||
### v07.00.00+ Priorities (2030+) - Modern Standards
|
||||
1. **Modern CSS**: Grid, Container Queries, Cascade Layers
|
||||
2. **Progressive Template**: Offline-capable assets and PWA features
|
||||
3. **Animation System**: Scroll-triggered effects and micro-interactions
|
||||
4. **Template Performance**: Advanced optimization and monitoring
|
||||
|
||||
---
|
||||
|
||||
## Long-term Vision
|
||||
|
||||
### Mission Statement
|
||||
MokoCassiopeia aims to be the **most developer-friendly, user-customizable, and standards-compliant Joomla template** while maintaining minimal core overrides for maximum upgrade compatibility.
|
||||
|
||||
### Core Principles
|
||||
1. **Non-Invasive**: Minimal Cassiopeia overrides
|
||||
2. **Standards-First**: MokoStandards compliance
|
||||
3. **Accessibility**: WCAG 2.1 compliance
|
||||
4. **Performance**: Fast, optimized delivery
|
||||
5. **Developer Experience**: Clear docs, easy setup, powerful tools
|
||||
6. **Template-Focused**: Pure template features without complex external dependencies
|
||||
|
||||
### 5-Year Strategic Roadmap (Template Features)
|
||||
|
||||
#### 2027 (v04.00.00) - Accessibility & Performance
|
||||
- Achieve WCAG 2.1 AA compliance for all template elements
|
||||
- Implement critical template performance optimizations
|
||||
- Enhance template layout system with flexible grids
|
||||
- Enable comprehensive development mode for template developers
|
||||
|
||||
#### 2028 (v05.00.00) - Layouts & Customization
|
||||
- Launch template-based layout builder system
|
||||
- Deploy extended styling and customization options
|
||||
- Enhance template component variations (headers, footers, menus)
|
||||
- Improve responsive design patterns for all devices
|
||||
|
||||
#### 2029 (v06.00.00) - Extensions & Enhancements
|
||||
- Introduce template addon and extension system
|
||||
- Launch template preset marketplace
|
||||
- Deploy advanced module styling and animation features
|
||||
- Implement comprehensive template SEO controls
|
||||
|
||||
#### 2030 (v07.00.00) - Modern Standards
|
||||
- Adopt modern CSS standards (Grid, Container Queries, Cascade Layers)
|
||||
- Implement progressive template features (PWA support)
|
||||
- Deploy advanced animation and interaction system
|
||||
- Enhance template performance monitoring and optimization
|
||||
|
||||
#### 2031 (v08.00.00) - Next-Generation Template
|
||||
- Advanced layout systems with subgrid support
|
||||
- Comprehensive template component library
|
||||
- Enhanced visual customization tools
|
||||
- Template ecosystem with child themes and derivatives
|
||||
|
||||
---
|
||||
|
||||
## External Resources
|
||||
|
||||
### Official Links
|
||||
- **Full Roadmap**: [https://mokoconsulting.tech/support/joomla-cms/mokocassiopeia-roadmap](https://mokoconsulting.tech/support/joomla-cms/mokocassiopeia-roadmap)
|
||||
- **Repository**: [https://git.mokoconsulting.tech/MokoConsulting/moko-cassiopeia](https://git.mokoconsulting.tech/MokoConsulting/moko-cassiopeia)
|
||||
- **Issue Tracker**: [GitHub Issues](https://git.mokoconsulting.tech/MokoConsulting/moko-cassiopeia/issues)
|
||||
- **Changelog**: [CHANGELOG.md](../CHANGELOG.md)
|
||||
|
||||
### Community
|
||||
- **Email Support**: hello@mokoconsulting.tech
|
||||
- **Contributing**: [CONTRIBUTING.md](../CONTRIBUTING.md)
|
||||
- **Code of Conduct**: [CODE_OF_CONDUCT.md](../CODE_OF_CONDUCT.md)
|
||||
|
||||
### Documentation
|
||||
- **Quick Start**: [docs/QUICK_START.md](./QUICK_START.md)
|
||||
- **Workflow Guide**: [docs/WORKFLOW_GUIDE.md](./WORKFLOW_GUIDE.md)
|
||||
- **Joomla Development**: [docs/JOOMLA_DEVELOPMENT.md](./JOOMLA_DEVELOPMENT.md)
|
||||
- **Main README**: [README.md](../README.md)
|
||||
|
||||
---
|
||||
|
||||
## Contributing to the Roadmap
|
||||
|
||||
Have ideas for future features? We welcome community input!
|
||||
|
||||
**How to Suggest Features**:
|
||||
1. Check the [GitHub Issues](https://git.mokoconsulting.tech/MokoConsulting/moko-cassiopeia/issues) for existing requests
|
||||
2. Open a new issue with the `enhancement` label
|
||||
3. Provide clear use cases and benefits
|
||||
4. Engage in community discussion
|
||||
|
||||
**Feature Evaluation Criteria**:
|
||||
- Alignment with core principles
|
||||
- User demand and use cases
|
||||
- Technical feasibility
|
||||
- Maintenance burden
|
||||
- Performance impact
|
||||
- Security implications
|
||||
|
||||
---
|
||||
|
||||
## Metadata
|
||||
|
||||
* Document: docs/ROADMAP.md
|
||||
* Repository: [https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia](https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia)
|
||||
* Path: /docs/ROADMAP.md
|
||||
* Owner: Moko Consulting
|
||||
* Version: 03.06.03
|
||||
* Status: Active
|
||||
* Effective Date: 2026-01-30
|
||||
* Classification: Public Open Source Documentation
|
||||
|
||||
## Revision History
|
||||
|
||||
| Date | Change Summary | Author |
|
||||
| ---------- | ----------------------------------------------------- | --------------- |
|
||||
| 2026-01-27 | Initial version-specific roadmap generated from codebase scan. | GitHub Copilot |
|
||||
| 2026-01-27 | Added 5-year future roadmap with annual major version releases (v04-v08). | GitHub Copilot |
|
||||
| 2026-01-27 | Refocused roadmap to concentrate on template-oriented features only. | GitHub Copilot |
|
||||
1. Download MokoOnyx from [Gitea Releases](https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/v01)
|
||||
2. Install via **System → Install → Extensions**
|
||||
3. Set MokoOnyx as default in **System → Site Templates**
|
||||
4. Visit any frontend page — settings are imported automatically
|
||||
5. Uninstall MokoCassiopeia from **Extensions → Manage**
|
||||
|
||||
@@ -1,360 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
*
|
||||
* This file is part of a Moko Consulting project.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
/**
|
||||
* Bridge migration helper — MokoCassiopeia → MokoOnyx
|
||||
*
|
||||
* Called from script.php during the v03.10.00 update. Copies the template
|
||||
* to the new directory name, migrates database records, and sets MokoOnyx
|
||||
* as the active site template.
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Filesystem\File;
|
||||
use Joomla\CMS\Filesystem\Folder;
|
||||
use Joomla\CMS\Log\Log;
|
||||
|
||||
class MokoBridgeMigration
|
||||
{
|
||||
private const OLD_NAME = 'mokocassiopeia';
|
||||
private const NEW_NAME = 'mokoonyx';
|
||||
|
||||
private const OLD_DISPLAY = 'MokoCassiopeia';
|
||||
private const NEW_DISPLAY = 'MokoOnyx';
|
||||
|
||||
/**
|
||||
* Run the full migration.
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public static function run(): bool
|
||||
{
|
||||
$app = Factory::getApplication();
|
||||
$db = Factory::getDbo();
|
||||
|
||||
// 1. Copy template files
|
||||
if (!self::copyTemplateFiles()) {
|
||||
$app->enqueueMessage(
|
||||
'MokoOnyx migration: failed to copy template files. '
|
||||
. 'You can manually copy templates/mokocassiopeia to templates/mokoonyx.',
|
||||
'error'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. Copy media files
|
||||
if (!self::copyMediaFiles()) {
|
||||
$app->enqueueMessage(
|
||||
'MokoOnyx migration: failed to copy media files. '
|
||||
. 'You can manually copy media/templates/site/mokocassiopeia to media/templates/site/mokoonyx.',
|
||||
'warning'
|
||||
);
|
||||
}
|
||||
|
||||
// 3. Rename internals in the new copy (templateDetails.xml, language files, etc.)
|
||||
self::renameInternals();
|
||||
|
||||
// 4. Register the new template in the database
|
||||
self::migrateDatabase($db);
|
||||
|
||||
// 5. Notify the admin
|
||||
$app->enqueueMessage(
|
||||
'<strong>MokoCassiopeia has been renamed to MokoOnyx.</strong><br>'
|
||||
. 'Your template settings have been migrated automatically. '
|
||||
. 'MokoOnyx is now your active site template. '
|
||||
. 'You can safely uninstall MokoCassiopeia from Extensions → Manage.',
|
||||
'success'
|
||||
);
|
||||
|
||||
self::log('Bridge migration completed successfully.');
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy template directory.
|
||||
*/
|
||||
private static function copyTemplateFiles(): bool
|
||||
{
|
||||
$src = JPATH_ROOT . '/templates/' . self::OLD_NAME;
|
||||
$dst = JPATH_ROOT . '/templates/' . self::NEW_NAME;
|
||||
|
||||
if (is_dir($dst)) {
|
||||
self::log('MokoOnyx template directory already exists — skipping copy.');
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!is_dir($src)) {
|
||||
self::log('Source template directory not found: ' . $src, 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
return Folder::copy($src, $dst);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy media directory.
|
||||
*/
|
||||
private static function copyMediaFiles(): bool
|
||||
{
|
||||
$src = JPATH_ROOT . '/media/templates/site/' . self::OLD_NAME;
|
||||
$dst = JPATH_ROOT . '/media/templates/site/' . self::NEW_NAME;
|
||||
|
||||
if (is_dir($dst)) {
|
||||
self::log('MokoOnyx media directory already exists — skipping copy.');
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!is_dir($src)) {
|
||||
self::log('Source media directory not found: ' . $src, 'warning');
|
||||
return true; // Non-critical
|
||||
}
|
||||
|
||||
return Folder::copy($src, $dst);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename internal references in the copied template.
|
||||
*/
|
||||
private static function renameInternals(): void
|
||||
{
|
||||
$base = JPATH_ROOT . '/templates/' . self::NEW_NAME;
|
||||
$mediaBase = JPATH_ROOT . '/media/templates/site/' . self::NEW_NAME;
|
||||
|
||||
// templateDetails.xml — name, element, update servers, paths
|
||||
$manifest = $base . '/templateDetails.xml';
|
||||
if (is_file($manifest)) {
|
||||
$content = file_get_contents($manifest);
|
||||
$content = str_replace(self::OLD_DISPLAY, self::NEW_DISPLAY, $content);
|
||||
$content = str_replace(self::OLD_NAME, self::NEW_NAME, $content);
|
||||
// Update the update server URLs to point to MokoOnyx repo
|
||||
$content = str_replace('MokoCassiopeia', 'MokoOnyx', $content);
|
||||
file_put_contents($manifest, $content);
|
||||
self::log('Updated templateDetails.xml for MokoOnyx.');
|
||||
}
|
||||
|
||||
// joomla.asset.json
|
||||
$assetFile = $base . '/joomla.asset.json';
|
||||
if (is_file($assetFile)) {
|
||||
$content = file_get_contents($assetFile);
|
||||
$content = str_replace(self::OLD_NAME, self::NEW_NAME, $content);
|
||||
$content = str_replace(self::OLD_DISPLAY, self::NEW_DISPLAY, $content);
|
||||
file_put_contents($assetFile, $content);
|
||||
}
|
||||
|
||||
// Language files
|
||||
$langDirs = [
|
||||
$base . '/language/en-GB',
|
||||
$base . '/language/en-US',
|
||||
];
|
||||
foreach ($langDirs as $langDir) {
|
||||
if (!is_dir($langDir)) continue;
|
||||
|
||||
foreach (glob($langDir . '/*mokocassiopeia*') as $file) {
|
||||
$newFile = str_replace(self::OLD_NAME, self::NEW_NAME, $file);
|
||||
if (is_file($file)) {
|
||||
$content = file_get_contents($file);
|
||||
$content = str_replace('MOKOCASSIOPEIA', 'MOKOONYX', $content);
|
||||
$content = str_replace(self::OLD_DISPLAY, self::NEW_DISPLAY, $content);
|
||||
$content = str_replace(self::OLD_NAME, self::NEW_NAME, $content);
|
||||
file_put_contents($newFile, $content);
|
||||
if ($newFile !== $file) {
|
||||
File::delete($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// script.php — class name
|
||||
$scriptFile = $base . '/script.php';
|
||||
if (is_file($scriptFile)) {
|
||||
$content = file_get_contents($scriptFile);
|
||||
$content = str_replace('Tpl_MokocassiopeiaInstallerScript', 'Tpl_MokoonyxInstallerScript', $content);
|
||||
$content = str_replace(self::OLD_DISPLAY, self::NEW_DISPLAY, $content);
|
||||
$content = str_replace(self::OLD_NAME, self::NEW_NAME, $content);
|
||||
// Remove the bridge migration call from the new template's script
|
||||
$content = preg_replace(
|
||||
'/\/\/ Bridge migration.*?MokoBridgeMigration::run\(\);/s',
|
||||
'// Migration complete — this is MokoOnyx',
|
||||
$content
|
||||
);
|
||||
file_put_contents($scriptFile, $content);
|
||||
}
|
||||
|
||||
// Remove bridge helper from the new template (not needed)
|
||||
$bridgeFile = $base . '/helper/bridge.php';
|
||||
if (is_file($bridgeFile)) {
|
||||
File::delete($bridgeFile);
|
||||
}
|
||||
|
||||
self::log('Renamed internal references in MokoOnyx.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate database records: template_styles, menu assignments.
|
||||
*/
|
||||
private static function migrateDatabase(\Joomla\Database\DatabaseInterface $db): void
|
||||
{
|
||||
// Get existing MokoCassiopeia styles
|
||||
$query = $db->getQuery(true)
|
||||
->select('*')
|
||||
->from('#__template_styles')
|
||||
->where($db->quoteName('template') . ' = ' . $db->quote(self::OLD_NAME))
|
||||
->where($db->quoteName('client_id') . ' = 0');
|
||||
$oldStyles = $db->setQuery($query)->loadObjectList();
|
||||
|
||||
if (empty($oldStyles)) {
|
||||
self::log('No MokoCassiopeia styles found in database.', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($oldStyles as $oldStyle) {
|
||||
// Check if MokoOnyx style already exists
|
||||
$query = $db->getQuery(true)
|
||||
->select('COUNT(*)')
|
||||
->from('#__template_styles')
|
||||
->where($db->quoteName('template') . ' = ' . $db->quote(self::NEW_NAME))
|
||||
->where($db->quoteName('title') . ' = ' . $db->quote(
|
||||
str_replace(self::OLD_DISPLAY, self::NEW_DISPLAY, $oldStyle->title)
|
||||
));
|
||||
$exists = (int) $db->setQuery($query)->loadResult();
|
||||
|
||||
if ($exists > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create new style with same params
|
||||
$newStyle = clone $oldStyle;
|
||||
unset($newStyle->id);
|
||||
$newStyle->template = self::NEW_NAME;
|
||||
$newStyle->title = str_replace(self::OLD_DISPLAY, self::NEW_DISPLAY, $oldStyle->title);
|
||||
|
||||
// Update params: replace any mokocassiopeia paths
|
||||
$params = $newStyle->params;
|
||||
if (is_string($params)) {
|
||||
$params = str_replace(self::OLD_NAME, self::NEW_NAME, $params);
|
||||
$newStyle->params = $params;
|
||||
}
|
||||
|
||||
$db->insertObject('#__template_styles', $newStyle, 'id');
|
||||
$newId = $newStyle->id;
|
||||
|
||||
// Copy menu assignments
|
||||
$query = $db->getQuery(true)
|
||||
->select('menuid')
|
||||
->from('#__template_styles_menus') // Joomla 5 uses this table
|
||||
->where('template_style_id = ' . (int) $oldStyle->id);
|
||||
|
||||
try {
|
||||
$menuIds = $db->setQuery($query)->loadColumn();
|
||||
foreach ($menuIds as $menuId) {
|
||||
$obj = (object) [
|
||||
'template_style_id' => $newId,
|
||||
'menuid' => $menuId,
|
||||
];
|
||||
$db->insertObject('#__template_styles_menus', $obj);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// Table may not exist in all Joomla versions
|
||||
}
|
||||
|
||||
// If this was the default style, make MokoOnyx the default
|
||||
if ($oldStyle->home == 1) {
|
||||
// Set MokoOnyx as default
|
||||
$query = $db->getQuery(true)
|
||||
->update('#__template_styles')
|
||||
->set($db->quoteName('home') . ' = 1')
|
||||
->where('id = ' . (int) $newId);
|
||||
$db->setQuery($query)->execute();
|
||||
|
||||
// Unset MokoCassiopeia as default
|
||||
$query = $db->getQuery(true)
|
||||
->update('#__template_styles')
|
||||
->set($db->quoteName('home') . ' = 0')
|
||||
->where('id = ' . (int) $oldStyle->id);
|
||||
$db->setQuery($query)->execute();
|
||||
|
||||
self::log('Set MokoOnyx as default site template.');
|
||||
}
|
||||
}
|
||||
|
||||
// Register the new template in the extensions table
|
||||
self::registerExtension($db);
|
||||
|
||||
self::log('Database migration completed. ' . count($oldStyles) . ' style(s) migrated.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register MokoOnyx in the extensions table so Joomla recognizes it.
|
||||
*/
|
||||
private static function registerExtension(\Joomla\Database\DatabaseInterface $db): void
|
||||
{
|
||||
// Check if already registered
|
||||
$query = $db->getQuery(true)
|
||||
->select('extension_id')
|
||||
->from('#__extensions')
|
||||
->where($db->quoteName('element') . ' = ' . $db->quote(self::NEW_NAME))
|
||||
->where($db->quoteName('type') . ' = ' . $db->quote('template'));
|
||||
$existing = $db->setQuery($query)->loadResult();
|
||||
|
||||
if ($existing) {
|
||||
self::log('MokoOnyx already registered in extensions table.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the old extension record as a base
|
||||
$query = $db->getQuery(true)
|
||||
->select('*')
|
||||
->from('#__extensions')
|
||||
->where($db->quoteName('element') . ' = ' . $db->quote(self::OLD_NAME))
|
||||
->where($db->quoteName('type') . ' = ' . $db->quote('template'));
|
||||
$oldExt = $db->setQuery($query)->loadObject();
|
||||
|
||||
if (!$oldExt) {
|
||||
self::log('MokoCassiopeia extension record not found.', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
$newExt = clone $oldExt;
|
||||
unset($newExt->extension_id);
|
||||
$newExt->element = self::NEW_NAME;
|
||||
$newExt->name = self::NEW_NAME;
|
||||
|
||||
// Update manifest_cache with new name
|
||||
$cache = json_decode($newExt->manifest_cache, true);
|
||||
if (is_array($cache)) {
|
||||
$cache['name'] = self::NEW_DISPLAY;
|
||||
$newExt->manifest_cache = json_encode($cache);
|
||||
}
|
||||
|
||||
$db->insertObject('#__extensions', $newExt, 'extension_id');
|
||||
self::log('Registered MokoOnyx in extensions table (ID: ' . $newExt->extension_id . ').');
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message.
|
||||
*/
|
||||
private static function log(string $message, string $priority = 'info'): void
|
||||
{
|
||||
$priorities = [
|
||||
'info' => Log::INFO,
|
||||
'warning' => Log::WARNING,
|
||||
'error' => Log::ERROR,
|
||||
];
|
||||
|
||||
Log::addLogger(
|
||||
['text_file' => 'mokocassiopeia_bridge.log.php'],
|
||||
Log::ALL,
|
||||
['mokocassiopeia_bridge']
|
||||
);
|
||||
|
||||
Log::add($message, $priorities[$priority] ?? Log::INFO, 'mokocassiopeia_bridge');
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
"defgroup": "Joomla.Template.Site",
|
||||
"ingroup": "MokoCassiopeia.Template.Assets",
|
||||
"path": "./media/templates/site/mokocassiopeia/joomla.asset.json",
|
||||
"version": "03.10.10",
|
||||
"version": "03.10.13",
|
||||
"brief": "Joomla asset registry for MokoCassiopeia"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2593,8 +2593,8 @@ progress {
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
color: var(--input-color, #e6ebf1);
|
||||
background-color: var(--input-bg, #1a2332);
|
||||
color: var(--input-color, #1a2332);
|
||||
background-color: var(--input-bg, #e6ebf1);
|
||||
background-clip: padding-box;
|
||||
border: 1px solid var(--input-border-color, #3a4250);
|
||||
-webkit-appearance: none;
|
||||
@@ -13912,7 +13912,7 @@ meter {
|
||||
height: 4px;
|
||||
margin: 1rem auto 2rem;
|
||||
content: "";
|
||||
background: var(--body-color, #e6ebf1);
|
||||
background: var(--body-bg, #e6ebf1);
|
||||
}
|
||||
|
||||
.container-banner .banner-overlay .overlay .text-thin .lead {
|
||||
@@ -14099,7 +14099,7 @@ td .form-control {
|
||||
margin: 0.5em;
|
||||
color: hsl(0, 0%, 0%);
|
||||
text-align: start;
|
||||
background: var(--body-color, #e6ebf1);
|
||||
background: var(--body-bg, #e6ebf1);
|
||||
border: 1px solid hsl(210, 7%, 46%);
|
||||
border-radius: 0.25rem;
|
||||
-webkit-box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.8);
|
||||
@@ -15549,7 +15549,7 @@ joomla-alert {
|
||||
min-height: 43px;
|
||||
padding: 0.25rem;
|
||||
color: var(--subhead-color, #9fa6ad);
|
||||
background: var(--body-color, #e6ebf1);
|
||||
background: var(--body-bg, #e6ebf1);
|
||||
-webkit-box-shadow: -3px -2px 22px var(--box-shadow-gray, #1a2027);
|
||||
box-shadow: -3px -2px 22px var(--box-shadow-gray, #1a2027);
|
||||
}
|
||||
@@ -15591,7 +15591,7 @@ joomla-alert {
|
||||
font-size: 1rem;
|
||||
line-height: 2.45rem;
|
||||
color: var(--subhead-color, #9fa6ad);
|
||||
background: var(--body-color, #e6ebf1);
|
||||
background: var(--body-bg, #e6ebf1);
|
||||
border-color: hsl(210, 11%, 71%);
|
||||
}
|
||||
|
||||
@@ -16330,7 +16330,7 @@ body:not(.has-sidebar-right) .site-grid .container-component {
|
||||
|
||||
.nav-tabs+.tab-content {
|
||||
padding: 0.9375rem;
|
||||
background: var(--body-color, #e6ebf1);
|
||||
background: var(--body-bg, #e6ebf1);
|
||||
border: 1px solid;
|
||||
border-color: hsl(210, 14%, 89%);
|
||||
border-radius: 0 0 0.25rem 0.25rem;
|
||||
@@ -16405,7 +16405,7 @@ body:not(.has-sidebar-right) .site-grid .container-component {
|
||||
}
|
||||
|
||||
.chosen-container.chosen-container-single .chosen-drop {
|
||||
background: var(--body-color, #e6ebf1);
|
||||
background: var(--body-bg, #e6ebf1);
|
||||
border: 1px solid hsl(210, 14%, 83%);
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 979 B |
Binary file not shown.
|
Before Width: | Height: | Size: 62 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 39 KiB |
464
src/script.php
464
src/script.php
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
|
||||
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
*
|
||||
* This file is part of a Moko Consulting project.
|
||||
*
|
||||
@@ -8,49 +8,40 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Template install/update/uninstall script.
|
||||
* Joomla calls the methods in this class automatically during template
|
||||
* install, update, and uninstall via the <scriptfile> element in
|
||||
* templateDetails.xml.
|
||||
* Joomla 5 and 6 compatible — uses the InstallerScriptInterface when
|
||||
* available, falls back to the legacy class-based approach otherwise.
|
||||
* MokoCassiopeia install/update/uninstall script.
|
||||
*
|
||||
* On update: copies the template as MokoOnyx (new directory), updates the
|
||||
* database to register MokoOnyx, migrates styles + params, and sets it as
|
||||
* the default site template. The old MokoCassiopeia directory stays intact
|
||||
* (Joomla's installer still needs it) — the user can uninstall it later.
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Installer\InstallerAdapter;
|
||||
use Joomla\CMS\Installer\InstallerScriptInterface;
|
||||
use Joomla\CMS\Log\Log;
|
||||
|
||||
class Tpl_MokocassiopeiaInstallerScript
|
||||
class Tpl_MokocassiopeiaInstallerScript implements InstallerScriptInterface
|
||||
{
|
||||
/**
|
||||
* Minimum PHP version required by this template.
|
||||
*/
|
||||
private const MIN_PHP = '8.1.0';
|
||||
|
||||
/**
|
||||
* Minimum Joomla version required by this template.
|
||||
*/
|
||||
private const MIN_PHP = '8.1.0';
|
||||
private const MIN_JOOMLA = '4.4.0';
|
||||
|
||||
/**
|
||||
* Called before install/update/uninstall.
|
||||
*
|
||||
* @param string $type install, update, discover_install, or uninstall.
|
||||
* @param InstallerAdapter $parent The adapter calling this method.
|
||||
*
|
||||
* @return bool True to proceed, false to abort.
|
||||
*/
|
||||
private const OLD_NAME = 'mokocassiopeia';
|
||||
private const NEW_NAME = 'mokoonyx';
|
||||
private const OLD_DISPLAY = 'MokoCassiopeia';
|
||||
private const NEW_DISPLAY = 'MokoOnyx';
|
||||
|
||||
private const ONYX_UPDATES_URL = 'https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/raw/branch/main/updates.xml';
|
||||
|
||||
// ── Joomla lifecycle ───────────────────────────────────────────────
|
||||
|
||||
public function preflight(string $type, InstallerAdapter $parent): bool
|
||||
{
|
||||
if (version_compare(PHP_VERSION, self::MIN_PHP, '<')) {
|
||||
Factory::getApplication()->enqueueMessage(
|
||||
sprintf(
|
||||
'MokoCassiopeia requires PHP %s or later. You are running PHP %s.',
|
||||
self::MIN_PHP,
|
||||
PHP_VERSION
|
||||
),
|
||||
sprintf('MokoCassiopeia requires PHP %s+. Running %s.', self::MIN_PHP, PHP_VERSION),
|
||||
'error'
|
||||
);
|
||||
return false;
|
||||
@@ -58,11 +49,7 @@ class Tpl_MokocassiopeiaInstallerScript
|
||||
|
||||
if (version_compare(JVERSION, self::MIN_JOOMLA, '<')) {
|
||||
Factory::getApplication()->enqueueMessage(
|
||||
sprintf(
|
||||
'MokoCassiopeia requires Joomla %s or later. You are running Joomla %s.',
|
||||
self::MIN_JOOMLA,
|
||||
JVERSION
|
||||
),
|
||||
sprintf('MokoCassiopeia requires Joomla %s+. Running %s.', self::MIN_JOOMLA, JVERSION),
|
||||
'error'
|
||||
);
|
||||
return false;
|
||||
@@ -71,160 +58,325 @@ class Tpl_MokocassiopeiaInstallerScript
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after a successful install.
|
||||
*
|
||||
* @param InstallerAdapter $parent The adapter calling this method.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function install(InstallerAdapter $parent): bool
|
||||
{
|
||||
$this->logMessage('MokoCassiopeia template installed.');
|
||||
$this->log('MokoCassiopeia installed.');
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after a successful update.
|
||||
*
|
||||
* This is where the CSS variable sync runs — it detects variables that
|
||||
* were added in the new version and injects them into the user's custom
|
||||
* palette files without overwriting existing values.
|
||||
*
|
||||
* @param InstallerAdapter $parent The adapter calling this method.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function update(InstallerAdapter $parent): bool
|
||||
{
|
||||
$this->logMessage('MokoCassiopeia template updated to v03.10.00 (bridge release).');
|
||||
$this->log('MokoCassiopeia update() — version ' . ($parent->getManifest()->version ?? '?'));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Run CSS variable sync to inject any new variables into user's custom palettes.
|
||||
$synced = $this->syncCustomVariables($parent);
|
||||
public function uninstall(InstallerAdapter $parent): bool
|
||||
{
|
||||
$this->log('MokoCassiopeia uninstalled.');
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($synced > 0) {
|
||||
Factory::getApplication()->enqueueMessage(
|
||||
sprintf(
|
||||
'MokoCassiopeia: %d new CSS variable(s) were added to your custom palette files. '
|
||||
. 'Review them in your light.custom.css and/or dark.custom.css to customise the new defaults.',
|
||||
$synced
|
||||
),
|
||||
'notice'
|
||||
);
|
||||
public function postflight(string $type, InstallerAdapter $parent): bool
|
||||
{
|
||||
// Debug: write directly to confirm script runs
|
||||
@file_put_contents(
|
||||
JPATH_ROOT . '/administrator/logs/bridge_debug.txt',
|
||||
date('Y-m-d H:i:s') . " postflight called, type={$type}\n",
|
||||
FILE_APPEND
|
||||
);
|
||||
|
||||
if ($type === 'update') {
|
||||
$this->log('=== MokoCassiopeia → MokoOnyx bridge ===');
|
||||
$this->bridge();
|
||||
}
|
||||
|
||||
// Bridge migration: MokoCassiopeia → MokoOnyx
|
||||
$bridgeScript = __DIR__ . '/helper/bridge.php';
|
||||
if (is_file($bridgeScript)) {
|
||||
require_once $bridgeScript;
|
||||
if (class_exists('MokoBridgeMigration')) {
|
||||
MokoBridgeMigration::run();
|
||||
return true;
|
||||
}
|
||||
|
||||
// ── Bridge ─────────────────────────────────────────────────────────
|
||||
|
||||
private function bridge(): void
|
||||
{
|
||||
$app = Factory::getApplication();
|
||||
|
||||
// 1. Copy template directory (don't rename — Joomla still needs the old one)
|
||||
$copied = $this->copyTemplateDir();
|
||||
if (!$copied && !is_dir(JPATH_ROOT . '/templates/' . self::NEW_NAME)) {
|
||||
$app->enqueueMessage(
|
||||
'MokoOnyx bridge: could not create template directory. '
|
||||
. 'Please copy <code>templates/mokocassiopeia</code> to <code>templates/mokoonyx</code> manually.',
|
||||
'warning'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Copy media directory
|
||||
$this->copyMediaDir();
|
||||
|
||||
// 3. Register MokoOnyx in #__extensions (if not already there)
|
||||
$this->registerExtension();
|
||||
|
||||
// 4. Migrate template styles (create MokoOnyx styles with same params)
|
||||
$this->migrateStyles();
|
||||
|
||||
// 5. Redirect update server to MokoOnyx
|
||||
$this->updateUpdateServer();
|
||||
|
||||
// 6. Notify
|
||||
$app->enqueueMessage(
|
||||
'<strong>MokoOnyx has been installed as a replacement for MokoCassiopeia.</strong><br>'
|
||||
. 'Your template settings have been migrated. MokoOnyx is now your active site template.<br>'
|
||||
. 'You can safely uninstall MokoCassiopeia from Extensions → Manage.',
|
||||
'success'
|
||||
);
|
||||
|
||||
$this->log('=== Bridge completed ===');
|
||||
}
|
||||
|
||||
// ── Copy directories ───────────────────────────────────────────────
|
||||
|
||||
private function copyTemplateDir(): bool
|
||||
{
|
||||
$src = JPATH_ROOT . '/templates/' . self::OLD_NAME;
|
||||
$dst = JPATH_ROOT . '/templates/' . self::NEW_NAME;
|
||||
|
||||
if (is_dir($dst)) {
|
||||
$this->log('Bridge: templates/' . self::NEW_NAME . ' already exists — skipping copy.');
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!is_dir($src)) {
|
||||
$this->log('Bridge: source template dir not found.', 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = $this->recursiveCopy($src, $dst);
|
||||
$this->log('Bridge: ' . ($result ? 'copied' : 'FAILED to copy') . ' template dir → ' . self::NEW_NAME);
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function copyMediaDir(): void
|
||||
{
|
||||
$src = JPATH_ROOT . '/media/templates/site/' . self::OLD_NAME;
|
||||
$dst = JPATH_ROOT . '/media/templates/site/' . self::NEW_NAME;
|
||||
|
||||
if (is_dir($dst)) {
|
||||
$this->log('Bridge: media dir already exists — skipping.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_dir($src)) {
|
||||
$this->log('Bridge: source media dir not found — skipping.');
|
||||
return;
|
||||
}
|
||||
|
||||
$result = $this->recursiveCopy($src, $dst);
|
||||
$this->log('Bridge: ' . ($result ? 'copied' : 'FAILED to copy') . ' media dir → ' . self::NEW_NAME);
|
||||
}
|
||||
|
||||
private function recursiveCopy(string $src, string $dst): bool
|
||||
{
|
||||
if (!mkdir($dst, 0755, true) && !is_dir($dst)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$dir = opendir($src);
|
||||
if ($dir === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (($file = readdir($dir)) !== false) {
|
||||
if ($file === '.' || $file === '..') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$srcPath = $src . '/' . $file;
|
||||
$dstPath = $dst . '/' . $file;
|
||||
|
||||
if (is_dir($srcPath)) {
|
||||
$this->recursiveCopy($srcPath, $dstPath);
|
||||
} else {
|
||||
copy($srcPath, $dstPath);
|
||||
}
|
||||
}
|
||||
|
||||
closedir($dir);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after a successful uninstall.
|
||||
*
|
||||
* @param InstallerAdapter $parent The adapter calling this method.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function uninstall(InstallerAdapter $parent): bool
|
||||
// ── Database updates ───────────────────────────────────────────────
|
||||
|
||||
private function registerExtension(): void
|
||||
{
|
||||
$this->logMessage('MokoCassiopeia template uninstalled.');
|
||||
return true;
|
||||
}
|
||||
$db = Factory::getDbo();
|
||||
|
||||
/**
|
||||
* Called after install/update completes (regardless of type).
|
||||
*
|
||||
* @param string $type install, update, or discover_install.
|
||||
* @param InstallerAdapter $parent The adapter calling this method.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function postflight(string $type, InstallerAdapter $parent): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Check if MokoOnyx is already registered
|
||||
$query = $db->getQuery(true)
|
||||
->select('extension_id')
|
||||
->from('#__extensions')
|
||||
->where($db->quoteName('element') . ' = ' . $db->quote(self::NEW_NAME))
|
||||
->where($db->quoteName('type') . ' = ' . $db->quote('template'));
|
||||
$exists = (int) $db->setQuery($query)->loadResult();
|
||||
|
||||
/**
|
||||
* Run the CSS variable sync utility.
|
||||
*
|
||||
* Loads sync_custom_vars.php from the template directory and calls
|
||||
* MokoCssVarSync::run() to detect and inject missing variables.
|
||||
*
|
||||
* @param InstallerAdapter $parent The adapter calling this method.
|
||||
*
|
||||
* @return int Number of variables added across all files.
|
||||
*/
|
||||
private function syncCustomVariables(InstallerAdapter $parent): int
|
||||
{
|
||||
$templateDir = $parent->getParent()->getPath('source');
|
||||
|
||||
// The sync script lives alongside this script in the template root.
|
||||
$syncScript = $templateDir . '/sync_custom_vars.php';
|
||||
|
||||
if (!is_file($syncScript)) {
|
||||
$this->logMessage('CSS variable sync script not found at: ' . $syncScript, 'warning');
|
||||
return 0;
|
||||
if ($exists) {
|
||||
$this->log('Bridge: MokoOnyx already registered in #__extensions (id=' . $exists . ').');
|
||||
return;
|
||||
}
|
||||
|
||||
require_once $syncScript;
|
||||
// Copy the MokoCassiopeia extension row and change element/name
|
||||
$query = $db->getQuery(true)
|
||||
->select('*')
|
||||
->from('#__extensions')
|
||||
->where($db->quoteName('element') . ' = ' . $db->quote(self::OLD_NAME))
|
||||
->where($db->quoteName('type') . ' = ' . $db->quote('template'));
|
||||
$oldExt = $db->setQuery($query)->loadObject();
|
||||
|
||||
if (!class_exists('MokoCssVarSync')) {
|
||||
$this->logMessage('MokoCssVarSync class not found after loading script.', 'warning');
|
||||
return 0;
|
||||
if (!$oldExt) {
|
||||
$this->log('Bridge: MokoCassiopeia not found in #__extensions.', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
$newExt = clone $oldExt;
|
||||
unset($newExt->extension_id);
|
||||
$newExt->element = self::NEW_NAME;
|
||||
$newExt->name = self::NEW_NAME;
|
||||
|
||||
// Update manifest_cache to reflect new name
|
||||
if (is_string($newExt->manifest_cache)) {
|
||||
$newExt->manifest_cache = str_replace(self::OLD_NAME, self::NEW_NAME, $newExt->manifest_cache);
|
||||
$newExt->manifest_cache = str_replace(self::OLD_DISPLAY, self::NEW_DISPLAY, $newExt->manifest_cache);
|
||||
}
|
||||
|
||||
try {
|
||||
$joomlaRoot = JPATH_ROOT;
|
||||
$results = MokoCssVarSync::run($joomlaRoot);
|
||||
|
||||
$totalAdded = 0;
|
||||
foreach ($results as $filePath => $result) {
|
||||
$totalAdded += count($result['added']);
|
||||
if (!empty($result['added'])) {
|
||||
$this->logMessage(
|
||||
sprintf(
|
||||
'CSS sync: added %d variable(s) to %s',
|
||||
count($result['added']),
|
||||
basename($filePath)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $totalAdded;
|
||||
$db->insertObject('#__extensions', $newExt, 'extension_id');
|
||||
$this->log('Bridge: registered MokoOnyx in #__extensions (id=' . $newExt->extension_id . ').');
|
||||
} catch (\Throwable $e) {
|
||||
$this->logMessage('CSS variable sync failed: ' . $e->getMessage(), 'error');
|
||||
return 0;
|
||||
$this->log('Bridge: failed to register extension: ' . $e->getMessage(), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message to Joomla's log system.
|
||||
*
|
||||
* @param string $message The log message.
|
||||
* @param string $priority Log priority (info, warning, error).
|
||||
*/
|
||||
private function logMessage(string $message, string $priority = 'info'): void
|
||||
private function migrateStyles(): void
|
||||
{
|
||||
$priorities = [
|
||||
'info' => Log::INFO,
|
||||
'warning' => Log::WARNING,
|
||||
'error' => Log::ERROR,
|
||||
];
|
||||
$db = Factory::getDbo();
|
||||
|
||||
Log::addLogger(
|
||||
['text_file' => 'mokocassiopeia.log.php'],
|
||||
Log::ALL,
|
||||
['mokocassiopeia']
|
||||
);
|
||||
// Get all MokoCassiopeia styles
|
||||
$query = $db->getQuery(true)
|
||||
->select('*')
|
||||
->from('#__template_styles')
|
||||
->where($db->quoteName('template') . ' = ' . $db->quote(self::OLD_NAME))
|
||||
->where($db->quoteName('client_id') . ' = 0');
|
||||
$oldStyles = $db->setQuery($query)->loadObjectList();
|
||||
|
||||
Log::add($message, $priorities[$priority] ?? Log::INFO, 'mokocassiopeia');
|
||||
if (empty($oldStyles)) {
|
||||
$this->log('Bridge: no MokoCassiopeia styles found.');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->log('Bridge: migrating ' . count($oldStyles) . ' style(s).');
|
||||
|
||||
foreach ($oldStyles as $old) {
|
||||
$newTitle = str_replace(self::OLD_DISPLAY, self::NEW_DISPLAY, $old->title);
|
||||
$newTitle = str_replace(self::OLD_NAME, self::NEW_NAME, $newTitle);
|
||||
|
||||
// Skip if MokoOnyx already has this style
|
||||
$check = $db->getQuery(true)
|
||||
->select('COUNT(*)')
|
||||
->from('#__template_styles')
|
||||
->where($db->quoteName('template') . ' = ' . $db->quote(self::NEW_NAME))
|
||||
->where($db->quoteName('title') . ' = ' . $db->quote($newTitle));
|
||||
if ((int) $db->setQuery($check)->loadResult() > 0) {
|
||||
$this->log("Bridge: style '{$newTitle}' already exists — skipping.");
|
||||
continue;
|
||||
}
|
||||
|
||||
$newParams = is_string($old->params)
|
||||
? str_replace(self::OLD_NAME, self::NEW_NAME, $old->params)
|
||||
: $old->params;
|
||||
|
||||
$new = clone $old;
|
||||
unset($new->id);
|
||||
$new->template = self::NEW_NAME;
|
||||
$new->title = $newTitle;
|
||||
$new->params = $newParams;
|
||||
$new->home = 0;
|
||||
|
||||
try {
|
||||
$db->insertObject('#__template_styles', $new, 'id');
|
||||
$newId = $new->id;
|
||||
|
||||
// If old was default, make new default
|
||||
if ($old->home == 1) {
|
||||
$db->setQuery(
|
||||
$db->getQuery(true)
|
||||
->update('#__template_styles')
|
||||
->set($db->quoteName('home') . ' = 1')
|
||||
->where('id = ' . (int) $newId)
|
||||
)->execute();
|
||||
|
||||
$db->setQuery(
|
||||
$db->getQuery(true)
|
||||
->update('#__template_styles')
|
||||
->set($db->quoteName('home') . ' = 0')
|
||||
->where('id = ' . (int) $old->id)
|
||||
)->execute();
|
||||
|
||||
$this->log('Bridge: set MokoOnyx as default site template.');
|
||||
}
|
||||
|
||||
$this->log("Bridge: created style '{$newTitle}'.");
|
||||
} catch (\Throwable $e) {
|
||||
$this->log("Bridge: failed to create style '{$newTitle}': " . $e->getMessage(), 'warning');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function updateUpdateServer(): void
|
||||
{
|
||||
$db = Factory::getDbo();
|
||||
|
||||
try {
|
||||
$query = $db->getQuery(true)
|
||||
->update('#__update_sites')
|
||||
->set($db->quoteName('location') . ' = ' . $db->quote(self::ONYX_UPDATES_URL))
|
||||
->set($db->quoteName('name') . ' = ' . $db->quote(self::NEW_DISPLAY))
|
||||
->where($db->quoteName('location') . ' LIKE ' . $db->quote('%MokoCassiopeia%'));
|
||||
$db->setQuery($query)->execute();
|
||||
|
||||
$n = $db->getAffectedRows();
|
||||
if ($n > 0) {
|
||||
$this->log("Bridge: redirected {$n} update site(s) to MokoOnyx.");
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$this->log('Bridge: update server redirect failed: ' . $e->getMessage(), 'warning');
|
||||
}
|
||||
|
||||
// Clear cached updates
|
||||
try {
|
||||
$db->setQuery(
|
||||
$db->getQuery(true)
|
||||
->delete('#__updates')
|
||||
->where($db->quoteName('element') . ' = ' . $db->quote(self::OLD_NAME))
|
||||
)->execute();
|
||||
} catch (\Throwable $e) {
|
||||
// Not critical
|
||||
}
|
||||
}
|
||||
|
||||
// ── Logging ────────────────────────────────────────────────────────
|
||||
|
||||
private function log(string $message, string $priority = 'info'): void
|
||||
{
|
||||
static $init = false;
|
||||
if (!$init) {
|
||||
Log::addLogger(
|
||||
['text_file' => 'mokocassiopeia_bridge.log.php'],
|
||||
Log::ALL,
|
||||
['mokocassiopeia_bridge']
|
||||
);
|
||||
$init = true;
|
||||
}
|
||||
|
||||
$levels = ['info' => Log::INFO, 'warning' => Log::WARNING, 'error' => Log::ERROR];
|
||||
Log::add($message, $levels[$priority] ?? Log::INFO, 'mokocassiopeia_bridge');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,13 +39,13 @@
|
||||
</server>
|
||||
</updateservers>
|
||||
<name>MokoCassiopeia</name>
|
||||
<version>03.10.10</version>
|
||||
<version>03.10.21</version>
|
||||
<scriptfile>script.php</scriptfile>
|
||||
<creationDate>2026-04-15</creationDate>
|
||||
<creationDate>2026-04-19</creationDate>
|
||||
<author>Jonathan Miller || Moko Consulting</author>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
<copyright>(C)GNU General Public License Version 3 - 2026 Moko Consulting</copyright>
|
||||
<description><![CDATA[<p><img src="https://img.shields.io/badge/version-03.10.10-blue.svg?logo=v&logoColor=white" alt="Version 03.10.10" /> <img src="https://img.shields.io/badge/license-GPL--3.0--or--later-green.svg?logo=gnu&logoColor=white" alt="License" /> <img src="https://img.shields.io/badge/Joomla-5.x%20%7C%206.x-red.svg?logo=joomla&logoColor=white" alt="Joomla" /> <img src="https://img.shields.io/badge/PHP-8.1%2B-777BB4.svg?logo=php&logoColor=white" alt="PHP" /></p> <h3>MokoCassiopeia Template Description</h3> <p> <strong>MokoCassiopeia</strong> continues Joomla's tradition of space-themed default templates— building on the legacy of <em>Solarflare</em> (Joomla 1.0), <em>Milkyway</em> (Joomla 1.5), and <em>Protostar</em> (Joomla 3.0). </p> <p> This template is a customized fork of the <strong>Cassiopeia</strong> template introduced in Joomla 4, preserving its modern, accessible, and mobile-first foundation while introducing new stylistic enhancements and structural refinements specifically tailored for use by Moko Consulting. </p> <h4>Custom Colour Themes</h4> <p> Starter palette files are included with the template. To create a custom colour scheme, copy <code>templates/mokocassiopeia/templates/light.custom.css</code> to <code>media/templates/site/mokocassiopeia/css/theme/light.custom.css</code>, or <code>templates/mokocassiopeia/templates/dark.custom.css</code> to <code>media/templates/site/mokocassiopeia/css/theme/dark.custom.css</code>. Customise the CSS variables to match your brand, then activate your palette in <em>System → Site Templates → MokoCassiopeia → Theme tab</em> by selecting "Custom" for the Light or Dark Mode Palette. A full variable reference is available in the <em>CSS Variables</em> tab in template options. </p> <h4>Custom CSS & JavaScript</h4> <p> For site-specific styles and scripts that should survive template updates, create the following files: </p> <ul> <li><code>media/templates/site/mokocassiopeia/css/user.css</code> — loaded on every page for custom CSS overrides.</li> <li><code>media/templates/site/mokocassiopeia/js/user.js</code> — loaded on every page for custom JavaScript.</li> </ul> <p> These files are gitignored and will not be overwritten by template updates. </p> <h4>Code Attribution</h4> <p> This template is based on the original <strong>Cassiopeia</strong> template developed by the <a href="https://www.joomla.org" target="_blank" rel="noopener">Joomla! Project</a> and released under the GNU General Public License. </p> <p> Modifications and enhancements have been made by Moko Consulting in accordance with open-source licensing standards. </p> <p> It includes integration with <a href="https://afeld.github.io/bootstrap-toc/" target="_blank" rel="noopener">Bootstrap TOC</a>, an open-source table of contents generator by A. Feld, licensed under the MIT License. </p> <p> All third-party libraries and assets remain the property of their respective authors and are credited within their source files where applicable. </p>]]></description>
|
||||
<description><![CDATA[<div style="padding:1.25rem;border:2px solid #dc2626;border-radius:8px;background:#fef2f2;margin-bottom:1rem;"> <h3 style="color:#991b1b;margin:0 0 .75rem;">⚠️ MokoCassiopeia has been renamed to MokoOnyx</h3> <p style="margin:0 0 1rem;"> <strong>This template is no longer maintained.</strong> Please install MokoOnyx manually to continue receiving updates. MokoOnyx has the same features and will automatically import your MokoCassiopeia settings on first install. </p> <p style="margin:0 0 1rem;"> <strong>Steps to migrate:</strong> </p> <ol style="margin:0 0 1rem 1.5rem;"> <li>Download MokoOnyx from <a href="https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/v01" style="color:#2563eb;font-weight:600;">Gitea Releases (v01)</a></li> <li>Install via <strong>System → Install → Extensions → Upload Package File</strong></li> <li>Set MokoOnyx as default in <strong>System → Site Templates</strong></li> <li>Visit any frontend page — your settings are imported automatically</li> <li>Uninstall MokoCassiopeia from <strong>Extensions → Manage</strong></li> </ol> <p style="margin:0;"> <a href="https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases" style="display:inline-block;padding:.5rem 1.25rem;background:#2563eb;color:#fff;border-radius:6px;text-decoration:none;font-weight:600;">Download MokoOnyx</a> </p></div> <p><img src="https://img.shields.io/badge/license-GPL--3.0--or--later-green.svg?logo=gnu&logoColor=white" alt="License" /> <img src="https://img.shields.io/badge/Joomla-5.x%20%7C%206.x-red.svg?logo=joomla&logoColor=white" alt="Joomla" /> <img src="https://img.shields.io/badge/PHP-8.1%2B-777BB4.svg?logo=php&logoColor=white" alt="PHP" /></p> <p> <strong>MokoCassiopeia</strong> is succeeded by <strong>MokoOnyx</strong> — same features, new name. All future development continues under the <a href="https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx">MokoOnyx project</a>. </p>]]></description>
|
||||
<inheritable>1</inheritable>
|
||||
<files>
|
||||
<filename>component.php</filename>
|
||||
|
||||
Reference in New Issue
Block a user