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.**
|
**`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.
|
- 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`).
|
- 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.
|
- Never hardcode a specific version in document body text — use the badge or FILE INFORMATION header only.
|
||||||
|
|
||||||
### Joomla Version Alignment
|
### 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
|
```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>
|
<updates>
|
||||||
<update>
|
<!-- 1. DEVELOPMENT --> <update>...<tag>development</tag>...</update>
|
||||||
<name>{{EXTENSION_NAME}}</name>
|
<!-- 2. ALPHA --> <update>...<tag>alpha</tag>...</update>
|
||||||
<version>01.02.04</version>
|
<!-- 3. BETA --> <update>...<tag>beta</tag>...</update>
|
||||||
<downloads>
|
<!-- 4. RC --> <update>...<tag>rc</tag>...</update>
|
||||||
<downloadurl type="full" format="zip">
|
<!-- 5. STABLE --> <update>...<tag>stable</tag>...</update>
|
||||||
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 … -->
|
|
||||||
</updates>
|
</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
|
## Joomla Extension Structure
|
||||||
@@ -286,11 +282,11 @@ Approved prefixes: `dev/` · `rc/` · `version/` · `patch/` · `copilot/` · `d
|
|||||||
| Change type | Documentation to update |
|
| Change type | Documentation to update |
|
||||||
|-------------|------------------------|
|
|-------------|------------------------|
|
||||||
| New or renamed PHP class/method | PHPDoc block; `docs/api/` entry |
|
| 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 or changed manifest.xml | Release workflow auto-bumps version across README.md, templateDetails.xml, and updates.xml |
|
||||||
| New release | Prepend `<update>` block to `updates.xml`; update CHANGELOG.md; bump README.md version |
|
| 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` |
|
| New or changed workflow | `docs/workflows/<workflow-name>.md` |
|
||||||
| Any modified file | Update the `VERSION` field in that file's `FILE INFORMATION` block |
|
| 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 |
|
# | 8. Build ZIP, upload asset, write SHA-256 to updates.xml |
|
||||||
# | |
|
# | |
|
||||||
# | Every version change: archives main -> version/XX.YY branch |
|
# | Every version change: archives main -> version/XX.YY branch |
|
||||||
# | Patch 00 = development (no release). First release = patch 01. |
|
# | All patches release (including 00). Patch 00/01 = full pipeline. |
|
||||||
# | First release only (patch == 01): |
|
# | First release only (patch == 01): |
|
||||||
# | 7b. Create new GitHub Release |
|
# | 7b. Create new GitHub Release |
|
||||||
# | |
|
# | |
|
||||||
@@ -100,19 +100,13 @@ jobs:
|
|||||||
echo "minor=$MINOR" >> "$GITHUB_OUTPUT"
|
echo "minor=$MINOR" >> "$GITHUB_OUTPUT"
|
||||||
echo "major=$MAJOR" >> "$GITHUB_OUTPUT"
|
echo "major=$MAJOR" >> "$GITHUB_OUTPUT"
|
||||||
echo "release_tag=v${MAJOR}" >> "$GITHUB_OUTPUT"
|
echo "release_tag=v${MAJOR}" >> "$GITHUB_OUTPUT"
|
||||||
if [ "$PATCH" = "00" ]; then
|
echo "skip=false" >> "$GITHUB_OUTPUT"
|
||||||
echo "skip=true" >> "$GITHUB_OUTPUT"
|
if [ "$PATCH" = "00" ] || [ "$PATCH" = "01" ]; then
|
||||||
echo "is_minor=false" >> "$GITHUB_OUTPUT"
|
echo "is_minor=true" >> "$GITHUB_OUTPUT"
|
||||||
echo "Version: $VERSION (patch 00 = development — skipping release)"
|
echo "Version: $VERSION (first release for this minor — full pipeline)"
|
||||||
else
|
else
|
||||||
echo "skip=false" >> "$GITHUB_OUTPUT"
|
echo "is_minor=false" >> "$GITHUB_OUTPUT"
|
||||||
if [ "$PATCH" = "01" ]; then
|
echo "Version: $VERSION (patch — platform version + badges only)"
|
||||||
echo "is_minor=true" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "Version: $VERSION (first release — full pipeline)"
|
|
||||||
else
|
|
||||||
echo "is_minor=false" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "Version: $VERSION (patch — platform version + badges only)"
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Check if already released
|
- name: Check if already released
|
||||||
|
|||||||
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 "sha256=${SHA256_HASH}" >> $GITHUB_OUTPUT
|
||||||
echo "SHA-256 Hash: ${SHA256_HASH}"
|
echo "SHA-256 Hash: ${SHA256_HASH}"
|
||||||
|
|
||||||
- name: Update updates.xml
|
- name: Determine stability channel
|
||||||
|
id: channel
|
||||||
run: |
|
run: |
|
||||||
TAG="${{ steps.tag.outputs.tag }}"
|
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)
|
DATE=$(date +%Y-%m-%d)
|
||||||
|
export PY_DATE="$DATE"
|
||||||
# Update version
|
|
||||||
sed -i "s|<version>.*</version>|<version>${TAG}</version>|" updates.xml
|
python3 << 'PYEOF'
|
||||||
|
import re, os
|
||||||
# Update creation date
|
|
||||||
sed -i "s|<creationDate>.*</creationDate>|<creationDate>${DATE}</creationDate>|" updates.xml
|
tag = os.environ["PY_TAG"]
|
||||||
|
sha256 = os.environ["PY_SHA"]
|
||||||
# Update download URL
|
date = os.environ["PY_DATE"]
|
||||||
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
|
stability = os.environ["PY_STABILITY"]
|
||||||
|
|
||||||
# Update or add SHA-256 hash
|
with open("updates.xml") as f:
|
||||||
if grep -q "<sha256>" updates.xml; then
|
content = f.read()
|
||||||
sed -i "s|<sha256>.*</sha256>|<sha256>sha256:${SHA256}</sha256>|" updates.xml
|
|
||||||
else
|
pattern = r"(<update>(?:(?!</update>).)*?<tag>" + re.escape(stability) + r"</tag>.*?</update>)"
|
||||||
# Add SHA-256 after downloadurl
|
match = re.search(pattern, content, re.DOTALL)
|
||||||
sed -i "/<\/downloadurl>/a\ <sha256>sha256:${SHA256}<\/sha256>" updates.xml
|
|
||||||
fi
|
if not match:
|
||||||
|
print(f"No <update> block for <tag>{stability}</tag> — skipping")
|
||||||
echo "Updated updates.xml with:"
|
exit(0)
|
||||||
echo " Version: ${TAG}"
|
|
||||||
echo " Date: ${DATE}"
|
block = match.group(1)
|
||||||
echo " SHA-256: ${SHA256}"
|
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
|
- name: Check for changes
|
||||||
id: changes
|
id: changes
|
||||||
@@ -118,8 +145,10 @@ jobs:
|
|||||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||||
git config --local user.name "gitea-actions[bot]"
|
git config --local user.name "gitea-actions[bot]"
|
||||||
|
|
||||||
|
STABILITY="${{ steps.channel.outputs.stability }}"
|
||||||
git add updates.xml
|
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
|
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 "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT"
|
||||||
echo "Building: ${ZIP_NAME} (${STABILITY})"
|
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
|
- name: Install dependencies
|
||||||
env:
|
env:
|
||||||
COMPOSER_AUTH: '{"http-basic":{"git.mokoconsulting.tech":{"username":"token","password":"${{ secrets.GA_TOKEN }}"}}}'
|
COMPOSER_AUTH: '{"http-basic":{"git.mokoconsulting.tech":{"username":"token","password":"${{ secrets.GA_TOKEN }}"}}}'
|
||||||
@@ -119,7 +190,7 @@ jobs:
|
|||||||
- name: Build ZIP
|
- name: Build ZIP
|
||||||
id: zip
|
id: zip
|
||||||
run: |
|
run: |
|
||||||
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
ZIP_NAME="${{ steps.bump.outputs.zip_name }}"
|
||||||
cd build/package
|
cd build/package
|
||||||
zip -r "../${ZIP_NAME}" .
|
zip -r "../${ZIP_NAME}" .
|
||||||
cd ..
|
cd ..
|
||||||
@@ -157,7 +228,7 @@ jobs:
|
|||||||
id: gitea_release
|
id: gitea_release
|
||||||
run: |
|
run: |
|
||||||
TAG="${{ steps.meta.outputs.tag_name }}"
|
TAG="${{ steps.meta.outputs.tag_name }}"
|
||||||
VERSION="${{ steps.meta.outputs.version }}"
|
VERSION="${{ steps.bump.outputs.version }}"
|
||||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||||
PRERELEASE="${{ steps.meta.outputs.prerelease }}"
|
PRERELEASE="${{ steps.meta.outputs.prerelease }}"
|
||||||
SHA256="${{ steps.zip.outputs.sha256 }}"
|
SHA256="${{ steps.zip.outputs.sha256 }}"
|
||||||
@@ -207,7 +278,7 @@ jobs:
|
|||||||
- name: "Gitea: Upload ZIP"
|
- name: "Gitea: Upload ZIP"
|
||||||
run: |
|
run: |
|
||||||
RELEASE_ID="${{ steps.gitea_release.outputs.release_id }}"
|
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 }}"
|
TOKEN="${{ secrets.GA_TOKEN }}"
|
||||||
API="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
API="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
|
|
||||||
@@ -225,9 +296,9 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
TAG="${{ steps.meta.outputs.tag_name }}"
|
TAG="${{ steps.meta.outputs.tag_name }}"
|
||||||
VERSION="${{ steps.meta.outputs.version }}"
|
VERSION="${{ steps.bump.outputs.version }}"
|
||||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||||
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
ZIP_NAME="${{ steps.bump.outputs.zip_name }}"
|
||||||
SHA256="${{ steps.zip.outputs.sha256 }}"
|
SHA256="${{ steps.zip.outputs.sha256 }}"
|
||||||
TOKEN="${{ secrets.GH_TOKEN }}"
|
TOKEN="${{ secrets.GH_TOKEN }}"
|
||||||
GH_REPO="mokoconsulting-tech/${GITEA_REPO}"
|
GH_REPO="mokoconsulting-tech/${GITEA_REPO}"
|
||||||
@@ -275,9 +346,9 @@ jobs:
|
|||||||
- name: "Update updates.xml for this channel"
|
- name: "Update updates.xml for this channel"
|
||||||
run: |
|
run: |
|
||||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||||
VERSION="${{ steps.meta.outputs.version }}"
|
VERSION="${{ steps.bump.outputs.version }}"
|
||||||
SHA256="${{ steps.zip.outputs.sha256 }}"
|
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 }}"
|
TAG="${{ steps.meta.outputs.tag_name }}"
|
||||||
DATE=$(date +%Y-%m-%d)
|
DATE=$(date +%Y-%m-%d)
|
||||||
|
|
||||||
@@ -332,8 +403,11 @@ jobs:
|
|||||||
# Update creation date
|
# Update creation date
|
||||||
block = re.sub(r"<creationDate>[^<]*</creationDate>", f"<creationDate>{date}</creationDate>", block)
|
block = re.sub(r"<creationDate>[^<]*</creationDate>", f"<creationDate>{date}</creationDate>", block)
|
||||||
|
|
||||||
# Update SHA-256
|
# Update or add SHA-256
|
||||||
block = re.sub(r"<sha256>[^<]*</sha256>", f"<sha256>{sha256}</sha256>", block)
|
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
|
# Update Gitea download URL
|
||||||
gitea_url = f"https://git.mokoconsulting.tech/{gitea_org}/{gitea_repo}/releases/download/{tag}/{zip_name}"
|
gitea_url = f"https://git.mokoconsulting.tech/{gitea_org}/{gitea_repo}/releases/download/{tag}/{zip_name}"
|
||||||
@@ -375,7 +449,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||||
VERSION="${{ steps.meta.outputs.version }}"
|
VERSION="${{ steps.bump.outputs.version }}"
|
||||||
CURRENT_BRANCH="${{ github.ref_name }}"
|
CURRENT_BRANCH="${{ github.ref_name }}"
|
||||||
TOKEN="${{ secrets.GA_TOKEN }}"
|
TOKEN="${{ secrets.GA_TOKEN }}"
|
||||||
|
|
||||||
@@ -428,9 +502,9 @@ jobs:
|
|||||||
|
|
||||||
- name: Summary
|
- name: Summary
|
||||||
run: |
|
run: |
|
||||||
VERSION="${{ steps.meta.outputs.version }}"
|
VERSION="${{ steps.bump.outputs.version }}"
|
||||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||||
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
ZIP_NAME="${{ steps.bump.outputs.zip_name }}"
|
||||||
SHA256="${{ steps.zip.outputs.sha256 }}"
|
SHA256="${{ steps.zip.outputs.sha256 }}"
|
||||||
TAG="${{ steps.meta.outputs.tag_name }}"
|
TAG="${{ steps.meta.outputs.tag_name }}"
|
||||||
|
|
||||||
|
|||||||
@@ -9,15 +9,15 @@
|
|||||||
INGROUP: MokoCassiopeia.Documentation
|
INGROUP: MokoCassiopeia.Documentation
|
||||||
REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia
|
REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia
|
||||||
FILE: ./README.md
|
FILE: ./README.md
|
||||||
VERSION: 03.10.10
|
VERSION: 03.10.21
|
||||||
BRIEF: Documentation for MokoCassiopeia template
|
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)
|
[](https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia/releases/tag/v03)
|
||||||
[](LICENSE)
|
[](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
|
# FILE INFORMATION
|
||||||
it under the terms of the GNU General Public License as published by
|
DEFGROUP: Joomla.Template.Site
|
||||||
the Free Software Foundation; either version 3 of the License, or
|
INGROUP: MokoCassiopeia.Documentation
|
||||||
(at your option) any later version.
|
REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia
|
||||||
|
PATH: /docs/ROADMAP.md
|
||||||
This program is distributed in the hope that it will be useful,
|
VERSION: 03.10.20
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
BRIEF: Redirect to MokoOnyx roadmap
|
||||||
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
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
# 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)
|
## Migration
|
||||||
- [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)
|
|
||||||
|
|
||||||
---
|
To migrate from MokoCassiopeia to MokoOnyx:
|
||||||
|
|
||||||
## Version Timeline
|
1. Download MokoOnyx from [Gitea Releases](https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/v01)
|
||||||
|
2. Install via **System → Install → Extensions**
|
||||||
### Past Releases
|
3. Set MokoOnyx as default in **System → Site Templates**
|
||||||
|
4. Visit any frontend page — settings are imported automatically
|
||||||
### v03.05.01 (2026-01-09) - Standards & Security
|
5. Uninstall MokoCassiopeia from **Extensions → Manage**
|
||||||
**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,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",
|
"defgroup": "Joomla.Template.Site",
|
||||||
"ingroup": "MokoCassiopeia.Template.Assets",
|
"ingroup": "MokoCassiopeia.Template.Assets",
|
||||||
"path": "./media/templates/site/mokocassiopeia/joomla.asset.json",
|
"path": "./media/templates/site/mokocassiopeia/joomla.asset.json",
|
||||||
"version": "03.10.10",
|
"version": "03.10.13",
|
||||||
"brief": "Joomla asset registry for MokoCassiopeia"
|
"brief": "Joomla asset registry for MokoCassiopeia"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2593,8 +2593,8 @@ progress {
|
|||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
color: var(--input-color, #e6ebf1);
|
color: var(--input-color, #1a2332);
|
||||||
background-color: var(--input-bg, #1a2332);
|
background-color: var(--input-bg, #e6ebf1);
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
border: 1px solid var(--input-border-color, #3a4250);
|
border: 1px solid var(--input-border-color, #3a4250);
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
@@ -13912,7 +13912,7 @@ meter {
|
|||||||
height: 4px;
|
height: 4px;
|
||||||
margin: 1rem auto 2rem;
|
margin: 1rem auto 2rem;
|
||||||
content: "";
|
content: "";
|
||||||
background: var(--body-color, #e6ebf1);
|
background: var(--body-bg, #e6ebf1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.container-banner .banner-overlay .overlay .text-thin .lead {
|
.container-banner .banner-overlay .overlay .text-thin .lead {
|
||||||
@@ -14099,7 +14099,7 @@ td .form-control {
|
|||||||
margin: 0.5em;
|
margin: 0.5em;
|
||||||
color: hsl(0, 0%, 0%);
|
color: hsl(0, 0%, 0%);
|
||||||
text-align: start;
|
text-align: start;
|
||||||
background: var(--body-color, #e6ebf1);
|
background: var(--body-bg, #e6ebf1);
|
||||||
border: 1px solid hsl(210, 7%, 46%);
|
border: 1px solid hsl(210, 7%, 46%);
|
||||||
border-radius: 0.25rem;
|
border-radius: 0.25rem;
|
||||||
-webkit-box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.8);
|
-webkit-box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.8);
|
||||||
@@ -15549,7 +15549,7 @@ joomla-alert {
|
|||||||
min-height: 43px;
|
min-height: 43px;
|
||||||
padding: 0.25rem;
|
padding: 0.25rem;
|
||||||
color: var(--subhead-color, #9fa6ad);
|
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);
|
-webkit-box-shadow: -3px -2px 22px var(--box-shadow-gray, #1a2027);
|
||||||
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;
|
font-size: 1rem;
|
||||||
line-height: 2.45rem;
|
line-height: 2.45rem;
|
||||||
color: var(--subhead-color, #9fa6ad);
|
color: var(--subhead-color, #9fa6ad);
|
||||||
background: var(--body-color, #e6ebf1);
|
background: var(--body-bg, #e6ebf1);
|
||||||
border-color: hsl(210, 11%, 71%);
|
border-color: hsl(210, 11%, 71%);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16330,7 +16330,7 @@ body:not(.has-sidebar-right) .site-grid .container-component {
|
|||||||
|
|
||||||
.nav-tabs+.tab-content {
|
.nav-tabs+.tab-content {
|
||||||
padding: 0.9375rem;
|
padding: 0.9375rem;
|
||||||
background: var(--body-color, #e6ebf1);
|
background: var(--body-bg, #e6ebf1);
|
||||||
border: 1px solid;
|
border: 1px solid;
|
||||||
border-color: hsl(210, 14%, 89%);
|
border-color: hsl(210, 14%, 89%);
|
||||||
border-radius: 0 0 0.25rem 0.25rem;
|
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 {
|
.chosen-container.chosen-container-single .chosen-drop {
|
||||||
background: var(--body-color, #e6ebf1);
|
background: var(--body-bg, #e6ebf1);
|
||||||
border: 1px solid hsl(210, 14%, 83%);
|
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
|
<?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.
|
* This file is part of a Moko Consulting project.
|
||||||
*
|
*
|
||||||
@@ -8,49 +8,40 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Template install/update/uninstall script.
|
* MokoCassiopeia install/update/uninstall script.
|
||||||
* Joomla calls the methods in this class automatically during template
|
*
|
||||||
* install, update, and uninstall via the <scriptfile> element in
|
* On update: copies the template as MokoOnyx (new directory), updates the
|
||||||
* templateDetails.xml.
|
* database to register MokoOnyx, migrates styles + params, and sets it as
|
||||||
* Joomla 5 and 6 compatible — uses the InstallerScriptInterface when
|
* the default site template. The old MokoCassiopeia directory stays intact
|
||||||
* available, falls back to the legacy class-based approach otherwise.
|
* (Joomla's installer still needs it) — the user can uninstall it later.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
defined('_JEXEC') or die;
|
defined('_JEXEC') or die;
|
||||||
|
|
||||||
use Joomla\CMS\Factory;
|
use Joomla\CMS\Factory;
|
||||||
use Joomla\CMS\Installer\InstallerAdapter;
|
use Joomla\CMS\Installer\InstallerAdapter;
|
||||||
|
use Joomla\CMS\Installer\InstallerScriptInterface;
|
||||||
use Joomla\CMS\Log\Log;
|
use Joomla\CMS\Log\Log;
|
||||||
|
|
||||||
class Tpl_MokocassiopeiaInstallerScript
|
class Tpl_MokocassiopeiaInstallerScript implements InstallerScriptInterface
|
||||||
{
|
{
|
||||||
/**
|
private const MIN_PHP = '8.1.0';
|
||||||
* Minimum PHP version required by this template.
|
|
||||||
*/
|
|
||||||
private const MIN_PHP = '8.1.0';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Minimum Joomla version required by this template.
|
|
||||||
*/
|
|
||||||
private const MIN_JOOMLA = '4.4.0';
|
private const MIN_JOOMLA = '4.4.0';
|
||||||
|
|
||||||
/**
|
private const OLD_NAME = 'mokocassiopeia';
|
||||||
* Called before install/update/uninstall.
|
private const NEW_NAME = 'mokoonyx';
|
||||||
*
|
private const OLD_DISPLAY = 'MokoCassiopeia';
|
||||||
* @param string $type install, update, discover_install, or uninstall.
|
private const NEW_DISPLAY = 'MokoOnyx';
|
||||||
* @param InstallerAdapter $parent The adapter calling this method.
|
|
||||||
*
|
private const ONYX_UPDATES_URL = 'https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/raw/branch/main/updates.xml';
|
||||||
* @return bool True to proceed, false to abort.
|
|
||||||
*/
|
// ── Joomla lifecycle ───────────────────────────────────────────────
|
||||||
|
|
||||||
public function preflight(string $type, InstallerAdapter $parent): bool
|
public function preflight(string $type, InstallerAdapter $parent): bool
|
||||||
{
|
{
|
||||||
if (version_compare(PHP_VERSION, self::MIN_PHP, '<')) {
|
if (version_compare(PHP_VERSION, self::MIN_PHP, '<')) {
|
||||||
Factory::getApplication()->enqueueMessage(
|
Factory::getApplication()->enqueueMessage(
|
||||||
sprintf(
|
sprintf('MokoCassiopeia requires PHP %s+. Running %s.', self::MIN_PHP, PHP_VERSION),
|
||||||
'MokoCassiopeia requires PHP %s or later. You are running PHP %s.',
|
|
||||||
self::MIN_PHP,
|
|
||||||
PHP_VERSION
|
|
||||||
),
|
|
||||||
'error'
|
'error'
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
@@ -58,11 +49,7 @@ class Tpl_MokocassiopeiaInstallerScript
|
|||||||
|
|
||||||
if (version_compare(JVERSION, self::MIN_JOOMLA, '<')) {
|
if (version_compare(JVERSION, self::MIN_JOOMLA, '<')) {
|
||||||
Factory::getApplication()->enqueueMessage(
|
Factory::getApplication()->enqueueMessage(
|
||||||
sprintf(
|
sprintf('MokoCassiopeia requires Joomla %s+. Running %s.', self::MIN_JOOMLA, JVERSION),
|
||||||
'MokoCassiopeia requires Joomla %s or later. You are running Joomla %s.',
|
|
||||||
self::MIN_JOOMLA,
|
|
||||||
JVERSION
|
|
||||||
),
|
|
||||||
'error'
|
'error'
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
@@ -71,160 +58,325 @@ class Tpl_MokocassiopeiaInstallerScript
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Called after a successful install.
|
|
||||||
*
|
|
||||||
* @param InstallerAdapter $parent The adapter calling this method.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function install(InstallerAdapter $parent): bool
|
public function install(InstallerAdapter $parent): bool
|
||||||
{
|
{
|
||||||
$this->logMessage('MokoCassiopeia template installed.');
|
$this->log('MokoCassiopeia installed.');
|
||||||
return true;
|
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
|
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.
|
public function uninstall(InstallerAdapter $parent): bool
|
||||||
$synced = $this->syncCustomVariables($parent);
|
{
|
||||||
|
$this->log('MokoCassiopeia uninstalled.');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if ($synced > 0) {
|
public function postflight(string $type, InstallerAdapter $parent): bool
|
||||||
Factory::getApplication()->enqueueMessage(
|
{
|
||||||
sprintf(
|
// Debug: write directly to confirm script runs
|
||||||
'MokoCassiopeia: %d new CSS variable(s) were added to your custom palette files. '
|
@file_put_contents(
|
||||||
. 'Review them in your light.custom.css and/or dark.custom.css to customise the new defaults.',
|
JPATH_ROOT . '/administrator/logs/bridge_debug.txt',
|
||||||
$synced
|
date('Y-m-d H:i:s') . " postflight called, type={$type}\n",
|
||||||
),
|
FILE_APPEND
|
||||||
'notice'
|
);
|
||||||
);
|
|
||||||
|
if ($type === 'update') {
|
||||||
|
$this->log('=== MokoCassiopeia → MokoOnyx bridge ===');
|
||||||
|
$this->bridge();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bridge migration: MokoCassiopeia → MokoOnyx
|
return true;
|
||||||
$bridgeScript = __DIR__ . '/helper/bridge.php';
|
}
|
||||||
if (is_file($bridgeScript)) {
|
|
||||||
require_once $bridgeScript;
|
// ── Bridge ─────────────────────────────────────────────────────────
|
||||||
if (class_exists('MokoBridgeMigration')) {
|
|
||||||
MokoBridgeMigration::run();
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// ── Database updates ───────────────────────────────────────────────
|
||||||
* Called after a successful uninstall.
|
|
||||||
*
|
private function registerExtension(): void
|
||||||
* @param InstallerAdapter $parent The adapter calling this method.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function uninstall(InstallerAdapter $parent): bool
|
|
||||||
{
|
{
|
||||||
$this->logMessage('MokoCassiopeia template uninstalled.');
|
$db = Factory::getDbo();
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// Check if MokoOnyx is already registered
|
||||||
* Called after install/update completes (regardless of type).
|
$query = $db->getQuery(true)
|
||||||
*
|
->select('extension_id')
|
||||||
* @param string $type install, update, or discover_install.
|
->from('#__extensions')
|
||||||
* @param InstallerAdapter $parent The adapter calling this method.
|
->where($db->quoteName('element') . ' = ' . $db->quote(self::NEW_NAME))
|
||||||
*
|
->where($db->quoteName('type') . ' = ' . $db->quote('template'));
|
||||||
* @return bool
|
$exists = (int) $db->setQuery($query)->loadResult();
|
||||||
*/
|
|
||||||
public function postflight(string $type, InstallerAdapter $parent): bool
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
if ($exists) {
|
||||||
* Run the CSS variable sync utility.
|
$this->log('Bridge: MokoOnyx already registered in #__extensions (id=' . $exists . ').');
|
||||||
*
|
return;
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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')) {
|
if (!$oldExt) {
|
||||||
$this->logMessage('MokoCssVarSync class not found after loading script.', 'warning');
|
$this->log('Bridge: MokoCassiopeia not found in #__extensions.', 'warning');
|
||||||
return 0;
|
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 {
|
try {
|
||||||
$joomlaRoot = JPATH_ROOT;
|
$db->insertObject('#__extensions', $newExt, 'extension_id');
|
||||||
$results = MokoCssVarSync::run($joomlaRoot);
|
$this->log('Bridge: registered MokoOnyx in #__extensions (id=' . $newExt->extension_id . ').');
|
||||||
|
|
||||||
$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;
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->logMessage('CSS variable sync failed: ' . $e->getMessage(), 'error');
|
$this->log('Bridge: failed to register extension: ' . $e->getMessage(), 'error');
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private function migrateStyles(): void
|
||||||
* 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
|
|
||||||
{
|
{
|
||||||
$priorities = [
|
$db = Factory::getDbo();
|
||||||
'info' => Log::INFO,
|
|
||||||
'warning' => Log::WARNING,
|
|
||||||
'error' => Log::ERROR,
|
|
||||||
];
|
|
||||||
|
|
||||||
Log::addLogger(
|
// Get all MokoCassiopeia styles
|
||||||
['text_file' => 'mokocassiopeia.log.php'],
|
$query = $db->getQuery(true)
|
||||||
Log::ALL,
|
->select('*')
|
||||||
['mokocassiopeia']
|
->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>
|
</server>
|
||||||
</updateservers>
|
</updateservers>
|
||||||
<name>MokoCassiopeia</name>
|
<name>MokoCassiopeia</name>
|
||||||
<version>03.10.10</version>
|
<version>03.10.21</version>
|
||||||
<scriptfile>script.php</scriptfile>
|
<scriptfile>script.php</scriptfile>
|
||||||
<creationDate>2026-04-15</creationDate>
|
<creationDate>2026-04-19</creationDate>
|
||||||
<author>Jonathan Miller || Moko Consulting</author>
|
<author>Jonathan Miller || Moko Consulting</author>
|
||||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||||
<copyright>(C)GNU General Public License Version 3 - 2026 Moko Consulting</copyright>
|
<copyright>(C)GNU General Public License Version 3 - 2026 Moko Consulting</copyright>
|
||||||
<description><![CDATA[<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>
|
<inheritable>1</inheritable>
|
||||||
<files>
|
<files>
|
||||||
<filename>component.php</filename>
|
<filename>component.php</filename>
|
||||||
|
|||||||
Reference in New Issue
Block a user