Verify favicon.ico from main branch is present #61
@@ -8,11 +8,15 @@
|
||||
DEFGROUP: Joomla.Template.Site
|
||||
INGROUP: Moko-Cassiopeia.Documentation
|
||||
PATH: ./CHANGELOG.md
|
||||
VERSION: 03.05.00
|
||||
VERSION: 03.06.00
|
||||
BRIEF: Changelog file documenting version history of Moko-Cassiopeia
|
||||
-->
|
||||
|
||||
# Changelog — Moko-Cassiopeia (VERSION: 03.05.00)
|
||||
# Changelog — Moko-Cassiopeia (VERSION: 03.06.00)
|
||||
|
||||
## [03.06.00] 2026-01-28
|
||||
### Changed
|
||||
- Updated version to 03.06.00 across all files
|
||||
|
||||
## [03.05.01] 2026-01-09
|
||||
### Added
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
INGROUP: Moko-Cassiopeia.Governance
|
||||
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
|
||||
FILE: CODE_OF_CONDUCT.md
|
||||
VERSION: 03.05.00
|
||||
VERSION: 03.06.00
|
||||
BRIEF: Contributor code of conduct for the Moko-Cassiopeia project.
|
||||
PATH: /CODE_OF_CONDUCT.md
|
||||
NOTE: This document defines behavioral expectations and enforcement processes.
|
||||
@@ -86,7 +86,7 @@ This project is managed from Tennessee, USA. This statement is informational and
|
||||
* **Repository:** [https://github.com/mokoconsulting-tech/moko-cassiopeia](https://github.com/mokoconsulting-tech/moko-cassiopeia)
|
||||
* **Path:** /CODE_OF_CONDUCT.md
|
||||
* **Owner:** Moko Consulting
|
||||
* **Version:** 03.05.00
|
||||
* **Version:** 03.06.00
|
||||
* **Status:** Active
|
||||
* **Effective Date:** 2025-12-18
|
||||
* **Last Reviewed:** 2025-12-18
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
INGROUP: Moko-Cassiopeia.Governance
|
||||
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
|
||||
FILE: CONTRIBUTING.md
|
||||
VERSION: 03.05.00
|
||||
VERSION: 03.06.00
|
||||
BRIEF: Contribution guidelines for the Moko-Cassiopeia project.
|
||||
PATH: /CONTRIBUTING.md
|
||||
NOTE: This document defines contribution workflow, standards, and governance alignment.
|
||||
@@ -133,7 +133,7 @@ Participation in this project is governed by the Code of Conduct. Unacceptable b
|
||||
* **Repository:** [https://github.com/mokoconsulting-tech/moko-cassiopeia](https://github.com/mokoconsulting-tech/moko-cassiopeia)
|
||||
* **Path:** /CONTRIBUTING.md
|
||||
* **Owner:** Moko Consulting
|
||||
* **Version:** 03.05.00
|
||||
* **Version:** 03.06.00
|
||||
* **Status:** Active
|
||||
* **Effective Date:** 2025-12-18
|
||||
* **Last Reviewed:** 2025-12-18
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
INGROUP: Moko-Cassiopeia.Governance
|
||||
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
|
||||
FILE: GOVERNANCE.md
|
||||
VERSION: 03.05.00
|
||||
VERSION: 03.06.00
|
||||
BRIEF: Project governance model, roles, and decision processes for Moko-Cassiopeia.
|
||||
PATH: /GOVERNANCE.md
|
||||
NOTE: This document defines authority, decision making, and escalation paths.
|
||||
@@ -103,7 +103,7 @@ This project is managed from Tennessee, USA. This statement is informational and
|
||||
* **Repository:** [https://github.com/mokoconsulting-tech/moko-cassiopeia](https://github.com/mokoconsulting-tech/moko-cassiopeia)
|
||||
* **Path:** /GOVERNANCE.md
|
||||
* **Owner:** Moko Consulting
|
||||
* **Version:** 03.05.00
|
||||
* **Version:** 03.06.00
|
||||
* **Status:** Active
|
||||
* **Effective Date:** 2025-12-18
|
||||
* **Last Reviewed:** 2025-12-18
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
INGROUP: Moko-Cassiopeia.Documentation
|
||||
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
|
||||
FILE: ./README.md
|
||||
VERSION: 03.05.00
|
||||
VERSION: 03.06.00
|
||||
BRIEF: Documentation for Moko-Cassiopeia template
|
||||
-->
|
||||
|
||||
# Moko-Cassiopeia (VERSION: 03.05.00)
|
||||
# Moko-Cassiopeia (VERSION: 03.06.00)
|
||||
|
||||
A modern, lightweight enhancement layer for Joomla's Cassiopeia
|
||||
template.
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
INGROUP: Moko-Cassiopeia.Governance
|
||||
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
|
||||
FILE: SECURITY.md
|
||||
VERSION: 03.05.00
|
||||
VERSION: 03.06.00
|
||||
BRIEF: Security policy and vulnerability reporting process for Moko-Cassiopeia.
|
||||
PATH: /SECURITY.md
|
||||
NOTE: This policy is process oriented and does not replace secure engineering practices.
|
||||
@@ -153,7 +153,7 @@ If you want credit, include the name or handle to list in an advisory. If you pr
|
||||
* **Repository:** [https://github.com/mokoconsulting-tech/moko-cassiopeia](https://github.com/mokoconsulting-tech/moko-cassiopeia)
|
||||
* **Path:** /SECURITY.md
|
||||
* **Owner:** Moko Consulting
|
||||
* **Version:** 03.05.00
|
||||
* **Version:** 03.06.00
|
||||
* **Status:** Active
|
||||
* **Effective Date:** 2025-12-18
|
||||
* **Last Reviewed:** 2025-12-18
|
||||
|
||||
@@ -271,7 +271,7 @@ make test
|
||||
|
||||
### Version Management
|
||||
|
||||
- Use semantic versioning: Major.Minor.Patch (03.05.00)
|
||||
- Use semantic versioning: Major.Minor.Patch (03.06.00)
|
||||
- Update CHANGELOG.md with all changes
|
||||
- Follow the version hierarchy: dev → rc → version → main
|
||||
- Never skip stages in the release process
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
INGROUP: Moko-Cassiopeia.Documentation
|
||||
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
|
||||
FILE: docs/README.md
|
||||
VERSION: 03.05.00
|
||||
VERSION: 03.06.00
|
||||
BRIEF: Documentation index for Moko-Cassiopeia template
|
||||
PATH: /docs/README.md
|
||||
-->
|
||||
@@ -41,7 +41,7 @@ This directory contains comprehensive documentation for the Moko-Cassiopeia Joom
|
||||
* Multi-version testing
|
||||
|
||||
* **[Roadmap](ROADMAP.md)** - Version-specific roadmap
|
||||
* Current features (v03.05.00)
|
||||
* Current features (v03.06.00)
|
||||
* Feature evolution timeline
|
||||
* Planned enhancements
|
||||
* Development priorities
|
||||
@@ -105,7 +105,7 @@ This project adheres to [MokoStandards](https://github.com/mokoconsulting-tech/M
|
||||
* Repository: [https://github.com/mokoconsulting-tech/moko-cassiopeia](https://github.com/mokoconsulting-tech/moko-cassiopeia)
|
||||
* Path: /docs/README.md
|
||||
* Owner: Moko Consulting
|
||||
* Version: 03.05.00
|
||||
* Version: 03.06.00
|
||||
* Status: Active
|
||||
* Effective Date: 2026-01-09
|
||||
|
||||
|
||||
@@ -10,12 +10,12 @@
|
||||
INGROUP: Moko-Cassiopeia.Documentation
|
||||
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
|
||||
FILE: docs/ROADMAP.md
|
||||
VERSION: 03.05.00
|
||||
VERSION: 03.06.00
|
||||
BRIEF: Version-specific roadmap for Moko-Cassiopeia template
|
||||
PATH: /docs/ROADMAP.md
|
||||
-->
|
||||
|
||||
# Moko-Cassiopeia Roadmap (VERSION: 03.05.00)
|
||||
# Moko-Cassiopeia Roadmap (VERSION: 03.06.00)
|
||||
|
||||
This document provides a comprehensive, version-specific roadmap for the Moko-Cassiopeia Joomla template, tracking feature evolution, current capabilities, and planned enhancements.
|
||||
|
||||
@@ -24,7 +24,7 @@ This document provides a comprehensive, version-specific roadmap for the Moko-Ca
|
||||
- [Version Timeline](#version-timeline)
|
||||
- [Past Releases](#past-releases)
|
||||
- [Future Roadmap (5-Year Plan)](#future-roadmap-5-year-plan)
|
||||
- [Current Release (v03.05.00)](#current-release-v030500)
|
||||
- [Current Release (v03.06.00)](#current-release-v030600)
|
||||
- [Implemented Features](#implemented-features)
|
||||
- [Planned Features](#planned-features)
|
||||
- [Development Priorities](#development-priorities)
|
||||
@@ -51,9 +51,15 @@ This document provides a comprehensive, version-specific roadmap for the Moko-Ca
|
||||
- Enforced repository compliance with MokoStandards
|
||||
- Improved security posture with automated scanning
|
||||
|
||||
### v03.05.00 (2026-01-04) - Workflow & Governance
|
||||
### 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
|
||||
@@ -431,7 +437,7 @@ The following versions represent our planned annual major releases, each buildin
|
||||
|
||||
---
|
||||
|
||||
## Current Release (v03.05.00)
|
||||
## Current Release (v03.06.00)
|
||||
|
||||
### System Requirements
|
||||
- **Joomla**: 4.4.x or 5.x
|
||||
@@ -835,7 +841,7 @@ Have ideas for future features? We welcome community input!
|
||||
* Repository: [https://github.com/mokoconsulting-tech/moko-cassiopeia](https://github.com/mokoconsulting-tech/moko-cassiopeia)
|
||||
* Path: /docs/ROADMAP.md
|
||||
* Owner: Moko Consulting
|
||||
* Version: 03.05.00
|
||||
* Version: 03.06.00
|
||||
* Status: Active
|
||||
* Last Updated: 2026-01-27
|
||||
* Classification: Public Open Source Documentation
|
||||
|
||||
@@ -134,7 +134,7 @@ codecept run
|
||||
**How to run:**
|
||||
1. Go to Actions → Create version branch
|
||||
2. Click "Run workflow"
|
||||
3. Enter version (e.g., 03.05.00)
|
||||
3. Enter version (e.g., 03.06.00)
|
||||
4. Select branch prefix (dev/, rc/, or version/)
|
||||
5. Click "Run workflow"
|
||||
|
||||
@@ -322,7 +322,7 @@ make validate-required
|
||||
git branch -r | grep dev/
|
||||
|
||||
# Delete remote branch if needed (carefully!)
|
||||
git push origin --delete dev/03.05.00
|
||||
git push origin --delete dev/03.06.00
|
||||
```
|
||||
|
||||
#### "Missing required secrets"
|
||||
@@ -381,7 +381,7 @@ phpcs --standard=phpcs.xml --report=source src/
|
||||
1. **Always use version branches:** dev/X.Y.Z, rc/X.Y.Z, version/X.Y.Z
|
||||
2. **Follow hierarchy:** dev → rc → version → main
|
||||
3. **Update CHANGELOG:** Document all changes in Unreleased section
|
||||
4. **Semantic versioning:** Major.Minor.Patch (03.05.00)
|
||||
4. **Semantic versioning:** Major.Minor.Patch (03.06.00)
|
||||
|
||||
### Code Quality
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
INGROUP: Moko-Cassiopeia
|
||||
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
|
||||
PATH: ./templates/moko-cassiopeia/component.php
|
||||
VERSION: 03.05.00
|
||||
VERSION: 03.06.00
|
||||
BRIEF: Main template index file for Moko-Cassiopeia rendering site layout
|
||||
*/
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
INGROUP: Moko-Cassiopeia
|
||||
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
|
||||
PATH: ./templates/moko-cassiopeia/custom.php
|
||||
VERSION: 03.05.00
|
||||
VERSION: 03.06.00
|
||||
BRIEF: Custom entry template file for Moko-Cassiopeia with user-defined overrides
|
||||
*/
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
INGROUP: Moko-Cassiopeia
|
||||
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
|
||||
PATH: ./templates/moko-cassiopeia/error.php
|
||||
VERSION: 03.05.00
|
||||
VERSION: 03.06.00
|
||||
BRIEF: Error page template file for Moko-Cassiopeia
|
||||
*/
|
||||
|
||||
@@ -26,72 +26,154 @@ $app = Factory::getApplication();
|
||||
$params = $this->params;
|
||||
|
|
||||
$wa = $this->getWebAssetManager();
|
||||
|
||||
// Template params
|
||||
$params_LightColorName = (string) $params->get('colorLightName', 'colors_standard'); // colors_standard|colors_alternative|colors_custom
|
||||
|
||||
$params_DarkColorName = (string) $params->get('colorDarkName', 'colors_standard'); // colors_standard|colors_alternative|colors_custom
|
||||
|
||||
$params_googletagmanager = $params->get('googletagmanager', false);
|
||||
$params_googletagmanagerid = $params->get('googletagmanagerid', '');
|
||||
$params_googleanalytics = $params->get('googleanalytics', false);
|
||||
$params_googleanalyticsid = $params->get('googleanalyticsid', '');
|
||||
$params_custom_head_start = $params->get('custom_head_start', '');
|
||||
$params_custom_head_end = $params->get('custom_head_end', '');
|
||||
$params_developmentmode = $params->get('developmentmode', false);
|
||||
|
||||
// Bootstrap behaviors (assets handled via WAM)
|
||||
HTMLHelper::_('bootstrap.framework');
|
||||
HTMLHelper::_('bootstrap.loadCss', true);
|
||||
HTMLHelper::_('bootstrap.alert');
|
||||
HTMLHelper::_('bootstrap.button');
|
||||
HTMLHelper::_('bootstrap.carousel');
|
||||
HTMLHelper::_('bootstrap.collapse');
|
||||
HTMLHelper::_('bootstrap.dropdown');
|
||||
HTMLHelper::_('bootstrap.modal');
|
||||
HTMLHelper::_('bootstrap.offcanvas');
|
||||
HTMLHelper::_('bootstrap.popover');
|
||||
HTMLHelper::_('bootstrap.scrollspy');
|
||||
HTMLHelper::_('bootstrap.tab');
|
||||
HTMLHelper::_('bootstrap.tooltip');
|
||||
HTMLHelper::_('bootstrap.toast');
|
||||
|
||||
// ------------------ Params ------------------
|
||||
$colorLight = (string) $params->get('colorLightName', 'colors_standard');
|
||||
$colorDark = (string) $params->get('colorDarkName', 'colors_standard');
|
||||
$themeFab = (int) $params->get('theme_fab_enabled', 1);
|
||||
$fABodyPos = (string) $params->get('theme_fab_pos', 'br');
|
||||
$gtmEnabled = (int) $params->get('googletagmanager', 0);
|
||||
$gtmId = (string) $params->get('googletagmanagerid', '');
|
||||
$fa6KitCode = (string) $params->get('fA6KitCode', '');
|
||||
$stickyHeader = (bool) $params->get('stickyHeader', 0);
|
||||
$brandEnabled = (int) $params->get('brand', 1);
|
||||
$siteDescription = (string) $params->get('siteDescription', '');
|
||||
|
||||
// Drawer icon params (escaped)
|
||||
$params_leftIcon = htmlspecialchars($params->get('drawerLeftIcon', 'fa-solid fa-chevron-right'), ENT_QUOTES, 'UTF-8');
|
||||
$params_rightIcon = htmlspecialchars($params->get('drawerRightIcon', 'fa-solid fa-chevron-left'), ENT_QUOTES, 'UTF-8');
|
||||
$params_leftIcon = htmlspecialchars($params->get('drawerLeftIcon', 'fa-solid fa-chevron-left'), ENT_QUOTES, 'UTF-8');
|
||||
$params_rightIcon = htmlspecialchars($params->get('drawerRightIcon', 'fa-solid fa-chevron-right'), ENT_QUOTES, 'UTF-8');
|
||||
|
||||
// ------------------ Styles ------------------
|
||||
$wa->useStyle('template.base');
|
||||
$wa->useStyle('template.user');
|
||||
// Template/Media path
|
||||
$templatePath = 'media/templates/site/moko-cassiopeia';
|
||||
|
||||
// Light/Dark variable sheets (load before consumers)
|
||||
if ($wa->assetExists('style', 'template.light.' . $colorLight)) {
|
||||
$wa->useStyle('template.light.' . $colorLight);
|
||||
// ===========================
|
||||
// Web Asset Manager (WAM) — matches your joomla.asset.json
|
||||
// ===========================
|
||||
|
||||
// Core template CSS
|
||||
$wa->useStyle('template.global.base'); // css/template.css
|
||||
$wa->useStyle('template.global.social-media-demo'); // css/global/social-media-demo.css
|
||||
|
||||
// Optional vendor CSS
|
||||
$wa->useStyle('vendor.bootstrap-toc');
|
||||
|
||||
// Color theme (light + optional dark)
|
||||
$colorLightKey = strtolower(preg_replace('/[^a-z0-9_.-]/i', '', $params_LightColorName));
|
||||
$colorDarkKey = strtolower(preg_replace('/[^a-z0-9_.-]/i', '', $params_DarkColorName));
|
||||
$lightKey = 'template.light.' . $colorLightKey;
|
||||
$darkKey = 'template.dark.' . $colorDarkKey;
|
||||
try {
|
||||
$wa->useStyle('template.light.colors_standard');
|
||||
} catch (\Throwable $e) {
|
||||
$wa->registerAndUseStyle('template.light.colors_standard', $templatePath . '/css/global/light/colors_standard.css');
|
||||
}
|
||||
if ($wa->assetExists('style', 'template.dark.' . $colorDark)) {
|
||||
$wa->useStyle('template.dark.' . $colorDark);
|
||||
try {
|
||||
$wa->useStyle('template.dark.colors_standard');
|
||||
} catch (\Throwable $e) {
|
||||
$wa->registerAndUseStyle('template.dark.colors_standard', $templatePath . '/css/global/dark/colors_standard.css');
|
||||
}
|
||||
try {
|
||||
$wa->useStyle($lightKey);
|
||||
} catch (\Throwable $e) {
|
||||
$wa->registerAndUseStyle('template.light.dynamic', $templatePath . '/css/global/light/' . $colorLightKey . '.css');
|
||||
}
|
||||
try {
|
||||
$wa->useStyle($darkKey);
|
||||
} catch (\Throwable $e) {
|
||||
$wa->registerAndUseStyle('template.dark.dynamic', $templatePath . '/css/global/dark/' . $colorDarkKey . '.css');
|
||||
}
|
||||
|
||||
// ------------------ Scripts ------------------
|
||||
// Scripts
|
||||
$wa->useScript('template.js');
|
||||
$wa->useScript('theme-init.js');
|
||||
if ($themeFab === 1) {
|
||||
$wa->useScript('darkmode-toggle.js');
|
||||
}
|
||||
if ($gtmEnabled === 1) {
|
||||
$wa->useScript('gtm.js');
|
||||
$wa->useScript('darkmode-toggle.js');
|
||||
$wa->useScript('vendor.bootstrap-toc.js');
|
||||
|
||||
// Meta
|
||||
$this->setMetaData('viewport', 'width=device-width, initial-scale=1');
|
||||
|
||||
if ($this->params->get('faKitCode')) {
|
||||
$faKit = "https://kit.fontawesome.com/" . $this->params->get('faKitCode') . ".js";
|
||||
HTMLHelper::_('script', $faKit, ['crossorigin' => 'anonymous']);
|
||||
} else {
|
||||
try {
|
||||
if ($params_developmentmode){
|
||||
$wa->useStyle('vendor.fa7free.all');
|
||||
$wa->useStyle('vendor.fa7free.brands');
|
||||
$wa->useStyle('vendor.fa7free.fontawesome');
|
||||
$wa->useStyle('vendor.fa7free.regular');
|
||||
$wa->useStyle('vendor.fa7free.solid');
|
||||
} else {
|
||||
$wa->useStyle('vendor.fa7free.all.min');
|
||||
$wa->useStyle('vendor.fa7free.brands.min');
|
||||
$wa->useStyle('vendor.fa7free.fontawesome.min');
|
||||
$wa->useStyle('vendor.fa7free.regular.min');
|
||||
$wa->useStyle('vendor.fa7free.solid.min');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
if ($params_developmentmode){
|
||||
$wa->registerAndUseStyle('vendor.fa7free.all.dynamic', $templatePath . '/vendor/fa7free/css/all.css');
|
||||
$wa->registerAndUseStyle('vendor.fa7free.brands.dynamic', $templatePath . '/vendor/fa7free/css/brands.css');
|
||||
$wa->registerAndUseStyle('vendor.fa7free.fontawesome.dynamic', $templatePath . '/vendor/fa7free/css/fontawesome.css');
|
||||
$wa->registerAndUseStyle('vendor.fa7free.regular.dynamic', $templatePath . '/vendor/fa7free/css/regular.css');
|
||||
$wa->registerAndUseStyle('vendor.fa7free.solid.dynamic', $templatePath . '/vendor/fa7free/css/solid.css');
|
||||
} else {
|
||||
$wa->registerAndUseStyle('vendor.fa7free.all.min.dynamic', $templatePath . '/vendor/fa7free/css/all.min.css');
|
||||
$wa->registerAndUseStyle('vendor.fa7free.brands.min.dynamic', $templatePath . '/vendor/fa7free/css/brands.min.css');
|
||||
$wa->registerAndUseStyle('vendor.fa7free.fontawesome.min.dynamic', $templatePath . '/vendor/fa7free/css/fontawesome.min.css');
|
||||
$wa->registerAndUseStyle('vendor.fa7free.regular.min.dynamic', $templatePath . '/vendor/fa7free/css/regular.min.css');
|
||||
$wa->registerAndUseStyle('vendor.fa7free.solid.min.dynamic', $templatePath . '/vendor/fa7free/css/solid.min.css');
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Optional Font Awesome 6 Kit (preferred) or FA5 fallback
|
||||
if (!empty($fa6KitCode)) {
|
||||
HTMLHelper::_('script', 'https://kit.fontawesome.com/' . rawurlencode($fa6KitCode) . '.js', [
|
||||
'crossorigin' => 'anonymous'
|
||||
]);
|
||||
} else {
|
||||
HTMLHelper::_('stylesheet', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css', ['version' => 'auto'], [
|
||||
'crossorigin' => 'anonymous',
|
||||
'referrerpolicy' => 'no-referrer',
|
||||
]);
|
||||
}
|
||||
$wa->useStyle('template.user'); // css/user.css
|
||||
|
||||
// ------------------ Context (logo, bootstrap needs) ------------------
|
||||
$sitename = htmlspecialchars($app->get('sitename'), ENT_QUOTES, 'UTF-8');
|
||||
|
||||
// Build logo/title
|
||||
if ($params->get('logoFile')) {
|
||||
$logo = HTMLHelper::_(
|
||||
// -------------------------------------
|
||||
// Brand: logo from params OR siteTitle
|
||||
// -------------------------------------
|
||||
$brandHtml = '';
|
||||
$logoFile = (string) $this->params->get('logoFile');
|
||||
|
||||
if ($logoFile !== '') {
|
||||
$brandHtml = HTMLHelper::_(
|
||||
'image',
|
||||
Uri::root(false) . htmlspecialchars($params->get('logoFile'), ENT_QUOTES),
|
||||
Uri::root(false) . htmlspecialchars($logoFile, ENT_QUOTES, 'UTF-8'),
|
||||
$sitename,
|
||||
['loading' => 'eager', 'decoding' => 'async'],
|
||||
['class' => 'logo d-inline-block', 'loading' => 'eager', 'decoding' => 'async'],
|
||||
false,
|
||||
0
|
||||
);
|
||||
} elseif ($params->get('siteTitle')) {
|
||||
$logo = '<span title="' . $sitename . '">' . htmlspecialchars($params->get('siteTitle'), ENT_COMPAT, 'UTF-8') . '</span>';
|
||||
} elseif ($this->params->get('siteTitle')) {
|
||||
$brandHtml = '<span class="site-title" title="' . $sitename . '">'
|
||||
. htmlspecialchars($this->params->get('siteTitle'), ENT_COMPAT, 'UTF-8')
|
||||
. '</span>';
|
||||
} else {
|
||||
$logo = HTMLHelper::_('image', 'full_logo.png', $sitename, ['class' => 'logo d-inline-block', 'loading' => 'eager', 'decoding' => 'async'], true, 0);
|
||||
// Fallback to a bundled image (relative to media paths)
|
||||
|
This file now loads Font Awesome 7 CSS (or a kit fallback). However, later in this template the button icons still use Font Awesome 5 class names ( This file now loads Font Awesome 7 CSS (or a kit fallback). However, later in this template the button icons still use Font Awesome 5 class names (`fas ...`). Without a v4/v5 shim these may not render. Either enable/include a compatibility shim for the kit/CSS, or update the template’s icon markup to the FA7 class syntax used elsewhere (e.g., `fa-solid ...`).
Fixed in commit Fixed in commit 336a3ae - updated icon classes from `fas` to `fa-solid` to match Font Awesome 7 syntax used throughout the template.
|
||||
$brandHtml = HTMLHelper::_('image', 'full_logo.png', $sitename, ['class' => 'logo d-inline-block', 'loading' => 'eager', 'decoding' => 'async'], true, 0);
|
||||
}
|
||||
|
||||
// ------------------ Error details ------------------
|
||||
@@ -103,23 +185,105 @@ $debugOn = defined('JDEBUG') && JDEBUG;
|
||||
<!DOCTYPE html>
|
||||
<html lang="<?php echo $this->language; ?>" dir="<?php echo $this->direction; ?>">
|
||||
<head>
|
||||
|
Same issue as Same issue as `custom_head_start`: `trim($params_custom_head_end)` can raise a deprecation warning on PHP 8.1+ when the param is `null` (default). Use an empty-string default/cast, or guard against `null` before calling `trim()`.
`trim($params_custom_head_start)` will emit a deprecation warning on PHP 8.1+ if the param is `null` (the default here is `null`). Consider defaulting these params to an empty string, casting to string before `trim()`, or using a non-null check (e.g. `!== null && trim(...) !== ''`).
Fixed in commit Fixed in commit 336a3ae - changed default values from `null` to empty string `''` for custom_head_start and custom_head_end parameters to prevent PHP 8.1+ deprecation warnings.
Fixed in commit Fixed in commit 336a3ae - changed default value from `null` to empty string `''` and removed trim() call.
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#ffffff" id="meta-theme-color" />
|
||||
<?php if ($params_custom_head_start !== '') : ?><?php echo $params_custom_head_start; ?><?php endif; ?>
|
||||
<jdoc:include type="head" />
|
||||
|
||||
<script>
|
||||
// Early theme application to avoid FOUC
|
||||
(function () {
|
||||
try {
|
||||
var stored = localStorage.getItem('theme');
|
||||
var prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
var theme = stored ? stored : (prefersDark ? 'dark' : 'light');
|
||||
document.documentElement.setAttribute('data-bs-theme', theme);
|
||||
} catch (e) {}
|
||||
})();
|
||||
</script>
|
||||
|
||||
<script>
|
||||
// Facebook in-app browser warning banner
|
||||
window.addEventListener('DOMContentLoaded', function () {
|
||||
var ua = navigator.userAgent || navigator.vendor || window.opera;
|
||||
var isFacebookBrowser = ua.indexOf('FBAN') > -1 || ua.indexOf('FBAV') > -1;
|
||||
if (isFacebookBrowser) {
|
||||
var warning = document.createElement('div');
|
||||
warning.textContent = '⚠️ KNOWN ISSUE: Images do not load in Facebook Web browser. Please open in external browser for full experience.';
|
||||
warning.style.position = 'fixed';
|
||||
warning.style.top = '0';
|
||||
warning.style.left = '0';
|
||||
warning.style.right = '0';
|
||||
warning.style.zIndex = '10000';
|
||||
warning.style.backgroundColor = '#007bff';
|
||||
warning.style.color = '#fff';
|
||||
warning.style.padding = '15px';
|
||||
warning.style.textAlign = 'center';
|
||||
warning.style.fontWeight = 'bold';
|
||||
warning.style.fontSize = '16px';
|
||||
warning.style.boxShadow = '0 2px 5px rgba(0,0,0,0.2)';
|
||||
document.body.appendChild(warning);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php if ($params_custom_head_end !== '') : ?><?php echo $params_custom_head_end; ?><?php endif; ?>
|
||||
</head>
|
||||
<body data-theme-fab-pos="<?php echo htmlspecialchars($fABodyPos, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<?php if ($gtmEnabled === 1 && !empty($gtmId)) : ?>
|
||||
<body data-bs-spy="scroll" data-bs-target="#toc" class="site error-page<?php
|
||||
echo ($this->direction == 'rtl' ? ' rtl' : '');
|
||||
?>">
|
||||
<?php if (!empty($params_googletagmanager) && !empty($params_googletagmanagerid)) : ?>
|
||||
<!-- Google Tag Manager -->
|
||||
<script>
|
||||
(function(w,d,s,l,i){
|
||||
w[l]=w[l]||[];
|
||||
w[l].push({'gtm.start': new Date().getTime(), event:'gtm.js'});
|
||||
var f=d.getElementsByTagName(s)[0],
|
||||
j=d.createElement(s),
|
||||
dl=l!='dataLayer'?'&l='+l:'';
|
||||
j.async=true;
|
||||
j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;
|
||||
f.parentNode.insertBefore(j,f);
|
||||
})(window,document,'script','dataLayer',<?php echo json_encode($params_googletagmanagerid, JSON_HEX_TAG | JSON_HEX_AMP); ?>);
|
||||
</script>
|
||||
<!-- End Google Tag Manager -->
|
||||
|
||||
<!-- Google Tag Manager (noscript) -->
|
||||
<noscript>
|
||||
<iframe src="https://www.googletagmanager.com/ns.html?id=<?php echo htmlspecialchars($gtmId, ENT_QUOTES, 'UTF-8'); ?>"
|
||||
<iframe src="https://www.googletagmanager.com/ns.html?id=<?php echo htmlspecialchars($params_googletagmanagerid, ENT_QUOTES, 'UTF-8'); ?>"
|
||||
height="0" width="0" style="display:none;visibility:hidden"></iframe>
|
||||
</noscript>
|
||||
<!-- End Google Tag Manager (noscript) -->
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- ========== DUPLICATED HEADER FROM INDEX ========== -->
|
||||
<header class="header container-header full-width<?php echo $stickyHeader ? ' position-sticky sticky-top' : ''; ?>">
|
||||
<?php if (!empty($params_googleanalytics) && !empty($params_googleanalyticsid)) : ?>
|
||||
<!-- Google Analytics (gtag.js) -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=<?php echo htmlspecialchars($params_googleanalyticsid, ENT_QUOTES, 'UTF-8'); ?>"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('consent', 'default', {
|
||||
'ad_storage': 'denied',
|
||||
'analytics_storage': 'granted',
|
||||
'ad_user_data': 'denied',
|
||||
'ad_personalization': 'denied'
|
||||
});
|
||||
(function(id){
|
||||
if (/^G-/.test(id)) {
|
||||
gtag('config', id, { 'anonymize_ip': true });
|
||||
} else if (/^UA-/.test(id)) {
|
||||
gtag('config', id, { 'anonymize_ip': true });
|
||||
console.warn('Using a UA- ID. Universal Analytics is sunset; consider migrating to GA4.');
|
||||
} else {
|
||||
console.warn('Unrecognized Google Analytics ID format:', id);
|
||||
}
|
||||
})(<?php echo json_encode($params_googleanalyticsid, JSON_HEX_TAG | JSON_HEX_AMP); ?>);
|
||||
</script>
|
||||
<!-- End Google Analytics -->
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- ========== HEADER FROM INDEX ========== -->
|
||||
<header class="header container-header full-width<?php echo $stickyHeader ? ' position-sticky sticky-top' : ''; ?>" role="banner">
|
||||
|
||||
<?php if ($this->countModules('topbar')) : ?>
|
||||
<div class="container-topbar">
|
||||
<jdoc:include type="modules" name="topbar" style="none" />
|
||||
@@ -133,14 +297,16 @@ $debugOn = defined('JDEBUG') && JDEBUG;
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($brandEnabled) : ?>
|
||||
<?php if ($this->params->get('brand', 1)) : ?>
|
||||
<div class="grid-child">
|
||||
<div class="navbar-brand">
|
||||
<a class="brand-logo" href="<?php echo $this->baseurl; ?>/">
|
||||
<?php echo $logo; ?>
|
||||
<?php echo $brandHtml; ?>
|
||||
</a>
|
||||
<?php if (!empty($siteDescription)) : ?>
|
||||
<div class="site-description"><?php echo htmlspecialchars($siteDescription); ?></div>
|
||||
<?php if ($this->params->get('siteDescription')) : ?>
|
||||
<div class="site-description">
|
||||
<?php echo htmlspecialchars($this->params->get('siteDescription'), ENT_QUOTES, 'UTF-8'); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
@@ -153,10 +319,33 @@ $debugOn = defined('JDEBUG') && JDEBUG;
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- Drawer Toggle Buttons -->
|
||||
<?php if ($this->countModules('drawer-left')) : ?>
|
||||
<button class="drawer-toggle-left btn btn-outline-secondary me-2"
|
||||
type="button"
|
||||
data-bs-toggle="offcanvas"
|
||||
data-bs-target="#drawer-left"
|
||||
aria-controls="drawer-left">
|
||||
<span class="<?php echo $params_leftIcon; ?>"></span>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($this->countModules('drawer-right')) : ?>
|
||||
<button class="drawer-toggle-right btn btn-outline-secondary"
|
||||
type="button"
|
||||
data-bs-toggle="offcanvas"
|
||||
data-bs-target="#drawer-right"
|
||||
aria-controls="drawer-right">
|
||||
<span class="<?php echo $params_rightIcon; ?>"></span>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($this->countModules('menu', true) || $this->countModules('search', true)) : ?>
|
||||
<div class="grid-child container-nav">
|
||||
<?php if ($this->countModules('menu', true)) : ?>
|
||||
<jdoc:include type="modules" name="menu" style="none" />
|
||||
<nav role="navigation" aria-label="Primary">
|
||||
<jdoc:include type="modules" name="menu" style="none" />
|
||||
</nav>
|
||||
<?php endif; ?>
|
||||
<?php if ($this->countModules('search', true)) : ?>
|
||||
<div class="container-search">
|
||||
@@ -166,7 +355,7 @@ $debugOn = defined('JDEBUG') && JDEBUG;
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</header>
|
||||
<!-- ========== END DUPLICATED HEADER ========== -->
|
||||
<!-- ========== END HEADER ========== -->
|
||||
|
||||
<main class="container my-4">
|
||||
<div class="card border-0 shadow-sm mb-4">
|
||||
@@ -186,11 +375,11 @@ $debugOn = defined('JDEBUG') && JDEBUG;
|
||||
|
||||
<div class="d-flex gap-2 flex-wrap">
|
||||
<a class="btn btn-primary" href="<?php echo htmlspecialchars(Uri::base(), ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<i class="fas fa-home me-1" aria-hidden="true"></i>
|
||||
<i class="fa-solid fa-home me-1" aria-hidden="true"></i>
|
||||
<?php echo Text::_('JERROR_LAYOUT_HOME_PAGE'); ?>
|
||||
</a>
|
||||
<button class="btn btn-outline-secondary" type="button" onclick="history.back();">
|
||||
<i class="fas fa-arrow-left me-1" aria-hidden="true"></i>
|
||||
<i class="fa-solid fa-arrow-left me-1" aria-hidden="true"></i>
|
||||
<?php echo Text::_('JPREV'); ?>
|
||||
</button>
|
||||
</div>
|
||||
@@ -245,7 +434,7 @@ $debugOn = defined('JDEBUG') && JDEBUG;
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
</main>
|
||||
<footer class="container-footer footer full-width py-4">
|
||||
<footer class="container-footer footer full-width">
|
||||
<?php if ($this->countModules('footer-menu', true)) : ?>
|
||||
<div class="grid-child footer-menu">
|
||||
<jdoc:include type="modules" name="footer-menu" />
|
||||
@@ -258,6 +447,37 @@ $debugOn = defined('JDEBUG') && JDEBUG;
|
||||
<?php endif; ?>
|
||||
</footer>
|
||||
|
||||
<?php if ($this->params->get('backTop') == 1) : ?>
|
||||
<a href="#top" id="back-top" class="back-to-top-link" aria-label="<?php echo Text::_('TPL_MOKO-CASSIOPEIA_BACKTOTOP'); ?>">
|
||||
<span class="icon-arrow-up icon-fw" aria-hidden="true"></span>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($this->countModules('drawer-left', true)) : ?>
|
||||
<!-- Left Offcanvas Drawer -->
|
||||
<aside class="offcanvas offcanvas-start" tabindex="-1" id="drawer-left">
|
||||
<div class="offcanvas-header">
|
||||
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="<?php echo Text::_('JLIB_HTML_BEHAVIOR_CLOSE'); ?>"></button>
|
||||
|
The GTM ID is embedded into an inline The GTM ID is embedded into an inline `<script>` as a JS string using `htmlspecialchars()`. HTML escaping is not sufficient for JavaScript string context (e.g., an ID containing a `'` can break out of the string). Use `json_encode($gtmID)` (or encode the raw param) when injecting into JS to ensure proper escaping for JS context.
Fixed in commit Fixed in commit 336a3ae - replaced `htmlspecialchars()` with `json_encode($params_googletagmanagerid, JSON_HEX_TAG | JSON_HEX_AMP)` for proper JavaScript context escaping.
|
||||
</div>
|
||||
<div class="offcanvas-body">
|
||||
<jdoc:include type="modules" name="drawer-left" style="none" />
|
||||
</div>
|
||||
</aside>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($this->countModules('drawer-right', true)) : ?>
|
||||
<!-- Right Offcanvas Drawer -->
|
||||
<aside class="offcanvas offcanvas-end" tabindex="-1" id="drawer-right">
|
||||
<div class="offcanvas-header">
|
||||
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="<?php echo Text::_('JLIB_HTML_BEHAVIOR_CLOSE'); ?>"></button>
|
||||
</div>
|
||||
<div class="offcanvas-body">
|
||||
<jdoc:include type="modules" name="drawer-right" style="none" />
|
||||
</div>
|
||||
</aside>
|
||||
<?php endif; ?>
|
||||
|
||||
<jdoc:include type="modules" name="debug" style="none" />
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
INGROUP: Moko-Cassiopeia
|
||||
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
|
||||
PATH: ./templates/moko-cassiopeia/html/com_content/article/toc-left.php
|
||||
VERSION: 03.05.00
|
||||
VERSION: 03.06.00
|
||||
BRIEF: Template override for Joomla articles with Table of Contents aligned left
|
||||
*/
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
INGROUP: Moko-Cassiopeia
|
||||
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
|
||||
PATH: ./templates/moko-cassiopeia/html/com_content/article/toc-right.php
|
||||
VERSION: 03.05.00
|
||||
VERSION: 03.06.00
|
||||
BRIEF: Template override for Joomla articles with Table of Contents aligned right
|
||||
*/
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
INGROUP: Moko-Cassiopeia
|
||||
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
|
||||
PATH: ./templates/moko-cassiopeia/index.php
|
||||
VERSION: 03.05.00
|
||||
VERSION: 03.06.00
|
||||
BRIEF: Main template index file for Moko-Cassiopeia rendering site layout
|
||||
*/
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"defgroup": "Joomla.Template.Site",
|
||||
"ingroup": "Moko-Cassiopeia.Template.Assets",
|
||||
"path": "./media/templates/site/moko-cassiopeia/joomla.asset.json",
|
||||
"version": "03.00.00",
|
||||
"version": "03.06.00",
|
||||
"brief": "Joomla asset registry for Moko-Cassiopeia"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
INGROUP: Moko-Cassiopeia
|
||||
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
|
||||
PATH: ./templates/moko-cassiopeia/offline.php
|
||||
VERSION: 03.05.00
|
||||
VERSION: 03.06.00
|
||||
BRIEF: Offline page template file for Moko-Cassiopeia
|
||||
*/
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
DEFGROUP: Joomla
|
||||
INGROUP: Moko-Cassiopeia
|
||||
PATH: templates/moko-cassiopeia/templateDetails.xml
|
||||
VERSION: 03.05.00
|
||||
VERSION: 03.06.00
|
||||
BRIEF: Template manifest XML file for Moko-Cassiopeia
|
||||
=========================================================================
|
||||
-->
|
||||
@@ -22,7 +22,7 @@
|
||||
</server>
|
||||
</updateservers>
|
||||
<name>moko-cassiopeia</name>
|
||||
<version>03.05.00</version>
|
||||
<version>03.06.00</version>
|
||||
<creationDate>2025-12-23</creationDate>
|
||||
<author>Jonathan Miller || Moko Consulting</author>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
INGROUP: Moko-Cassiopeia
|
||||
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
|
||||
PATH: ./updates.xml
|
||||
VERSION: 03.05.00
|
||||
VERSION: 03.06.00
|
||||
BRIEF: Update manifest XML file for Moko-Cassiopeia
|
||||
-->
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<type>template</type>
|
||||
<client>site</client>
|
||||
|
||||
<version>03.05.00</version>
|
||||
<version>03.06.00</version>
|
||||
<creationDate>2025-12-12</creationDate>
|
||||
<author>Jonathan Miller || Moko Consulting</author>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
|
||||
The inline comment for
template.global.social-media-demosays it loadscss/user.css, but insrc/templates/joomla.asset.jsonthis asset points tocss/global/social-media-demo.css. Please correct the comment to avoid confusion when tracing which CSS is being loaded.$brandEnabledand$siteDescriptionare now assigned but no longer used in this template (brand/description are read directly from$this->paramslater). Please remove these unused variables (or switch the later checks to use them) to avoid dead code and keep params handling consistent.Fixed in commit
336a3ae- corrected comment to reflect actual asset pathcss/global/social-media-demo.css.Fixed in commit
336a3ae- removed unused variables$brandEnabledand$siteDescriptionas they are accessed directly from$this->paramsin the template.