Update Bootstrap TOC to v1.0.1 release reference and specify Soft Offline Mode with persistent links #69

Merged
Copilot merged 10 commits from copilot/update-name-to-mokocassiopeia into main 2026-01-30 01:35:46 +00:00
80 changed files with 1593 additions and 5213 deletions

1
.gitignore vendored
View File

@@ -138,6 +138,7 @@ package-lock.json
# PHP / Composer tooling
# ============================================================
vendor/
!src/media/vendor/
composer.lock
*.phar
codeception.phar

View File

@@ -6,19 +6,63 @@
# FILE INFORMATION
DEFGROUP: Joomla.Template.Site
INGROUP: Moko-Cassiopeia.Documentation
INGROUP: MokoCassiopeia.Documentation
PATH: ./CHANGELOG.md
VERSION: 03.06.00
BRIEF: Changelog file documenting version history of Moko-Cassiopeia
VERSION: 03.06.02
BRIEF: Changelog file documenting version history of MokoCassiopeia
-->
# Changelog — Moko-Cassiopeia (VERSION: 03.06.00)
# Changelog — MokoCassiopeia (VERSION: 03.06.02)
All notable changes to the MokoCassiopeia Joomla template are documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [03.06.02] - 2026-01-30
### Major Rebrand
This release includes a complete rebrand from "Moko-Cassiopeia" (hyphenated) to "MokoCassiopeia" (camelCase).
### Changed
- **Naming Convention**: Changed template identifier from `moko-cassiopeia` to `mokocassiopeia` across all files
- **Display Name**: Updated from "Moko-Cassiopeia" to "MokoCassiopeia" in all documentation and language files
- **Language Constants**: Renamed all language keys from `TPL_MOKO-CASSIOPEIA_*` to `TPL_MOKOCASSIOPEIA_*`
- **Language Files**: Renamed from `tpl_moko-cassiopeia.*` to `tpl_mokocassiopeia.*` (4 files)
- **Media Paths**: Updated from `media/templates/site/moko-cassiopeia/` to `media/templates/site/mokocassiopeia/`
- **Repository URLs**: Updated all references to use `MokoCassiopeia` casing
- **Template Element**: Changed Joomla extension element name from `moko-cassiopeia` to `mokocassiopeia`
- **Documentation**: Updated all markdown files, XML manifests, and code comments
### Removed
- **Default Assets**: Removed `logo.svg` and `favicon.ico` to allow clean installations
- **Template Overrides**: Removed all template override files (48 files, ~4,500 lines)
- Removed `src/templates/html/` folder entirely
- Removed overrides for: com_content, com_contact, com_engage, mod_menu, mod_custom, mod_gabble, layouts/chromes
- Template now inherits all rendering from Joomla Cassiopeia defaults
- Updated `templateDetails.xml` to remove html folder reference
### Breaking Changes
⚠️ **Important**: This release contains breaking changes:
- Existing installations will see template name change in Joomla admin
- Custom code referencing old language constants (`TPL_MOKO-CASSIOPEIA_*`) will need updates
- Custom code referencing old media paths will need updates
- Sites relying on custom template overrides will revert to Cassiopeia defaults
- Extension element name changed (may require reinstallation in some cases)
### Migration Notes
- Backup your site before upgrading
- Review any custom code for references to old naming convention
- Test thoroughly after upgrade, especially if using custom overrides
## [03.06.00] - 2026-01-28
## [03.06.00] 2026-01-28
### Changed
- Updated version to 03.06.00 across all files
- Standardized version numbering format
## [03.05.01] - 2026-01-09
## [03.05.01] 2026-01-09
### Added
- Added `dependency-review.yml` workflow for dependency vulnerability scanning
- Added `standards-compliance.yml` workflow for MokoStandards validation
@@ -27,71 +71,91 @@
### Changed
- Removed custom `codeql-analysis.yml` workflow (repository uses GitHub's default CodeQL setup)
### Changed
- Enforced repository compliance with MokoStandards requirements
- Improved security posture with automated scanning and dependency management
## [03.05.00] 2026-01-04
- Created `.github/workflows`
## [03.05.00] - 2026-01-04
### Added
- Created `.github/workflows` directory structure
### Changed
- Replaced `./CODE_OF_CONDUCT.md` from `MokoStandards`
- Replaced `./CONTRIBUTING.md` from `MokoStandards`
- TODO split to own file
## [03.01.00] 2025-12-16
- Created `.github/workflows/`
## [03.01.00] - 2025-12-16
## [03.00] 2025-12-09
### Removed
- `./CODE_OF_CONDUCT.md`
- `./CONTRIBUTING.md`
### Updated
- Copyright Headers to MokoCodingDefaults standards
- Fixed `./templates/moko-cassiopeia/index.php` color style injection
- Upgraded `FontAwesome 6` to `FontAwesome 7 Free`
- Added `Font Awesome 7 Free` style fallback
## [02.01.05] 2025-09-04
- Removed vmbasic.css
- Repaired temaplte.css and colors_standard.css
## [2.00.00] 2025-08-30
### Added
* **Dark Mode Toggle**
* Frontend toggle switch included in template.
* JavaScript handles switching between light/dark modes.
* Dark mode CSS rules applied across template styles.
* Automatic persistence of user choice (via localStorage).
- Created `.github/workflows/` directory for GitHub Actions
* **Header Parameters Update**
## [03.00.00] - 2025-12-09
* Added **logo parameter support** in template settings.
* Updated metadata & copyright header.
### Changed
- Copyright Headers updated to MokoCodingDefaults standards
- Fixed `./templates/mokocassiopeia/index.php` color style injection
- Upgraded Font Awesome 6 to Font Awesome 7 Free
- Added Font Awesome 7 Free style fallback
* **Expanded TOC (Table of Contents)**
### Removed
- Removed `./CODE_OF_CONDUCT.md` (replaced with MokoStandards version)
- Removed `./CONTRIBUTING.md` (replaced with MokoStandards version)
* Automatic TOC injection when enabled.
* User selects placement via article > options > layout (`toc-left` or `toc-right`).
## [02.01.05] - 2025-09-04
### Updated
### Changed
- Repaired template.css and colors_standard.css
* Cleaned up `index.php` by removing **skip-to-content** duplicate calls.
* Consolidated JavaScript asset loading (ensuring dark-mode script is loaded correctly from external JS file).
* Streamlined CSS for **toggle switch**, ensuring it inherits Bootstrap/Cassiopeia defaults.
* General accessibility refinements in typography and color contrast.
* Fixed missing **logo param** in header output.
* Corrected stylesheet inconsistencies between Bootstrap 5 helpers and template overrides.
* Patched redundant calls in script includes.
### Removed
- Removed vmbasic.css
## 01.00.00
## [02.00.00] - 2025-08-30
* **Initial Public Release** with:
### Added - Dark Mode Toggle
- Frontend toggle switch included in template
- JavaScript handles switching between light/dark modes
- Dark mode CSS rules applied across template styles
- Automatic persistence of user choice (via localStorage)
- Admins can override default mode in template settings
* Font Awesome 6
* Bootstrap 5 helpers
* Automatic Table of Contents (TOC) utility
* Moko Expansions: Google Tag Manager / GA4 hooks
### Added - Header Parameters Update
- Added logo parameter support in template settings
- Updated metadata & copyright header
For the full development roadmap, visit:
[Moko-Cassiopeia Roadmap](https://mokoconsulting.tech/support/joomla-cms/moko-cassiopeia-roadmap)
### Added - Expanded TOC (Table of Contents)
- Automatic TOC injection when enabled
- User selects placement via article > options > layout (`toc-left` or `toc-right`)
### Changed
- Cleaned up `index.php` by removing skip-to-content duplicate calls
- Consolidated JavaScript asset loading (ensuring dark-mode script is loaded correctly from external JS file)
- Streamlined CSS for toggle switch, ensuring it inherits Bootstrap/Cassiopeia defaults
- General accessibility refinements in typography and color contrast
- Fixed missing logo param in header output
- Corrected stylesheet inconsistencies between Bootstrap 5 helpers and template overrides
- Patched redundant calls in script includes
## [01.00.00] - 2025-01-01
### Added - Initial Public Release
- Font Awesome 6 integration (later upgraded to FA7)
- Bootstrap 5 helpers (grid, utility classes)
- Automatic Table of Contents (TOC) utility
- Moko Expansions: Google Tag Manager / GA4 hooks
- Built on top of Joomla's default Cassiopeia template
- Minimal core template overrides for maximum upgrade compatibility
---
## Links
- **Full Roadmap**: [MokoCassiopeia Roadmap](https://mokoconsulting.tech/support/joomla-cms/mokocassiopeia-roadmap)
- **Repository**: [GitHub](https://github.com/mokoconsulting-tech/MokoCassiopeia)
- **Issue Tracker**: [GitHub Issues](https://github.com/mokoconsulting-tech/MokoCassiopeia/issues)
## Version Format
This project uses semantic versioning: `MAJOR.MINOR.PATCH`
- **MAJOR**: Incompatible API changes or major overhauls
- **MINOR**: New features, backwards-compatible
- **PATCH**: Bug fixes, backwards-compatible

View File

@@ -7,18 +7,18 @@
# FILE INFORMATION
DEFGROUP: Joomla.Template
INGROUP: Moko-Cassiopeia.Governance
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
INGROUP: MokoCassiopeia.Governance
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
FILE: CODE_OF_CONDUCT.md
VERSION: 03.06.00
BRIEF: Contributor code of conduct for the Moko-Cassiopeia project.
VERSION: 03.06.02
BRIEF: Contributor code of conduct for the MokoCassiopeia project.
PATH: /CODE_OF_CONDUCT.md
NOTE: This document defines behavioral expectations and enforcement processes.
-->
## Code of Conduct
This Code of Conduct establishes expectations for behavior within the Moko-Cassiopeia project community. The objective is to maintain a professional, inclusive, and respectful environment aligned with open source governance best practices.
This Code of Conduct establishes expectations for behavior within the MokoCassiopeia project community. The objective is to maintain a professional, inclusive, and respectful environment aligned with open source governance best practices.
## Scope
@@ -56,7 +56,7 @@ Project maintainers are responsible for:
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported through:
* Email: `hello@mokoconsulting.tech` with subject `CODE OF CONDUCT: Moko-Cassiopeia`.
* Email: `hello@mokoconsulting.tech` with subject `CODE OF CONDUCT: MokoCassiopeia`.
Reports should include relevant context, links, screenshots, or other supporting information.
@@ -83,7 +83,7 @@ This project is managed from Tennessee, USA. This statement is informational and
## Metadata
* **Document:** CODE_OF_CONDUCT.md
* **Repository:** [https://github.com/mokoconsulting-tech/moko-cassiopeia](https://github.com/mokoconsulting-tech/moko-cassiopeia)
* **Repository:** [https://github.com/mokoconsulting-tech/MokoCassiopeia](https://github.com/mokoconsulting-tech/MokoCassiopeia)
* **Path:** /CODE_OF_CONDUCT.md
* **Owner:** Moko Consulting
* **Version:** 03.06.00

View File

@@ -7,18 +7,18 @@
# FILE INFORMATION
DEFGROUP: Joomla.Template
INGROUP: Moko-Cassiopeia.Governance
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
INGROUP: MokoCassiopeia.Governance
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
FILE: CONTRIBUTING.md
VERSION: 03.06.00
BRIEF: Contribution guidelines for the Moko-Cassiopeia project.
VERSION: 03.06.02
BRIEF: Contribution guidelines for the MokoCassiopeia project.
PATH: /CONTRIBUTING.md
NOTE: This document defines contribution workflow, standards, and governance alignment.
-->
## Contributing
This document defines how to contribute to the Moko-Cassiopeia project. The goal is to ensure changes are reviewable, auditable, and aligned with project governance and release processes.
This document defines how to contribute to the MokoCassiopeia project. The goal is to ensure changes are reviewable, auditable, and aligned with project governance and release processes.
## Scope
@@ -43,8 +43,8 @@ For first-time contributors:
```bash
# Clone the repository
git clone https://github.com/mokoconsulting-tech/moko-cassiopeia.git
cd moko-cassiopeia
git clone https://github.com/mokoconsulting-tech/MokoCassiopeia.git
cd MokoCassiopeia
```
See [docs/QUICK_START.md](./docs/QUICK_START.md) for detailed setup instructions.
@@ -130,7 +130,7 @@ Participation in this project is governed by the Code of Conduct. Unacceptable b
## Metadata
* **Document:** CONTRIBUTING.md
* **Repository:** [https://github.com/mokoconsulting-tech/moko-cassiopeia](https://github.com/mokoconsulting-tech/moko-cassiopeia)
* **Repository:** [https://github.com/mokoconsulting-tech/MokoCassiopeia](https://github.com/mokoconsulting-tech/MokoCassiopeia)
* **Path:** /CONTRIBUTING.md
* **Owner:** Moko Consulting
* **Version:** 03.06.00

View File

@@ -7,22 +7,22 @@
# FILE INFORMATION
DEFGROUP: Joomla.Template
INGROUP: Moko-Cassiopeia.Governance
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
INGROUP: MokoCassiopeia.Governance
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
FILE: GOVERNANCE.md
VERSION: 03.06.00
BRIEF: Project governance model, roles, and decision processes for Moko-Cassiopeia.
VERSION: 03.06.02
BRIEF: Project governance model, roles, and decision processes for MokoCassiopeia.
PATH: /GOVERNANCE.md
NOTE: This document defines authority, decision making, and escalation paths.
-->
## Governance Overview
This document defines the governance framework for the Moko-Cassiopeia project. The objective is to ensure clear ownership, predictable decision making, and accountable stewardship across development, releases, and community interaction.
This document defines the governance framework for the MokoCassiopeia project. The objective is to ensure clear ownership, predictable decision making, and accountable stewardship across development, releases, and community interaction.
## Project Ownership
Moko-Cassiopeia is owned and maintained by **Moko Consulting**. Final authority for project direction, releases, and policy enforcement resides with the project owner.
MokoCassiopeia is owned and maintained by **Moko Consulting**. Final authority for project direction, releases, and policy enforcement resides with the project owner.
## Roles and Responsibilities
@@ -100,7 +100,7 @@ This project is managed from Tennessee, USA. This statement is informational and
## Metadata
* **Document:** GOVERNANCE.md
* **Repository:** [https://github.com/mokoconsulting-tech/moko-cassiopeia](https://github.com/mokoconsulting-tech/moko-cassiopeia)
* **Repository:** [https://github.com/mokoconsulting-tech/MokoCassiopeia](https://github.com/mokoconsulting-tech/MokoCassiopeia)
* **Path:** /GOVERNANCE.md
* **Owner:** Moko Consulting
* **Version:** 03.06.00

626
README.md
View File

@@ -6,172 +6,484 @@
# FILE INFORMATION
DEFGROUP: Joomla.Template.Site
INGROUP: Moko-Cassiopeia.Documentation
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
INGROUP: MokoCassiopeia.Documentation
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
FILE: ./README.md
VERSION: 03.06.00
BRIEF: Documentation for Moko-Cassiopeia template
VERSION: 03.06.02
BRIEF: Documentation for MokoCassiopeia template
-->
# Moko-Cassiopeia (VERSION: 03.06.00)
# MokoCassiopeia
A modern, lightweight enhancement layer for Joomla's Cassiopeia
template.
Moko-Cassiopeia adds **Font Awesome 7**, **Bootstrap 5** helpers, an
automatic **Table of Contents (TOC)** utility, and optional **Moko
Expansions** including **Google Tag Manager** and **Google Analytics
(GA4)** hooks---all while keeping core template overrides minimal and
upgrade-friendly.
**Version:** 03.06.02
**A Modern, Lightweight Joomla Template Based on Cassiopeia**
## Table of Contents
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
[![Joomla](https://img.shields.io/badge/Joomla-4.4.x%20%7C%205.x-blue.svg)](https://www.joomla.org)
[![PHP](https://img.shields.io/badge/PHP-8.0%2B-blue.svg)](https://www.php.net)
- [Features](#features)
- [Requirements](#requirements)
- [Quick Start](#quick-start)
- [Installation](#installation)
- [Configuration](#configuration)
- [Global Params](#global-params)
- [Font Awesome 6](#font-awesome-6)
- [Bootstrap 5 Helpers](#bootstrap-5-helpers)
- [Table of Contents](#table-of-contents)
- [Dark Mode Toggle](#dark-mode-toggle)
- [Soft Offline Mode](#soft-offline-mode)
- [Development](#development)
- [Changelog](#changelog)
- [Roadmap](#roadmap)
## Features
### Core Enhancements
- Built on top of Joomla's default **Cassiopeia** template.
- **Font Awesome 6** integration.
- **Bootstrap 5** helpers (grid, utility classes).
- **Automatic TOC** insertion for articles (activated via layout
`toc-left` or `toc-right`).
### Added in 2.0
- **Dark Mode Toggle**
- User-facing switch in the header.
- Persists preference with local storage.
- Admins can override default mode in template settings.
- **Improved Template Params**
- Configure logo, GTM container ID, and dark mode defaults
directly from template settings.
### New in 2.1.5 (In Development)
- **Soft Offline Mode**
- Keeps articles in specific categories available when the site is
offline.
- Example: legal or policy documents remain publicly viewable even
during maintenance.
- Admin can configure which categories remain accessible.
## Requirements
- Joomla **4.4.x** or **5.x**
- PHP **8.0+**
- MySQL/MariaDB compatible database
## Quick Start
1. Install `moko-cassiopeia.zip` via Joomla's Template Installer.
2. Set **Moko-Cassiopeia** as your default template.
3. Configure template options under **System → Site Templates →
Moko-Cassiopeia**.
## Installation
Upload and install through Joomla's extension manager.
If upgrading from a prior version, Joomla will safely overwrite files
--- no manual uninstall required.
## Configuration
### Global Params
- **Logo**: Upload a custom site logo.
- **Color Scheme**: Toggle light/dark defaults.
- **Analytics/GTM**: Enable/disable optional expansions.
### Custom Color Palettes
Moko-Cassiopeia supports custom color schemes for both light and dark modes:
- **Standard**: Default Joomla Cassiopeia colors
- **Alternative**: Alternative color palette
- **Custom**: Create your own custom colors by adding `colors_custom.css` files
To use custom colors:
1. Create `src/media/css/colors/light/colors_custom.css` for light mode
2. Create `src/media/css/colors/dark/colors_custom.css` for dark mode
3. Define your CSS variables in these files (see existing `colors_standard.css` for reference)
4. Select "Custom" in template settings under **Variables & Palettes**
### Font Awesome 7
- Fully integrated into Joomla's asset manager.
- No extra scripts required.
### Bootstrap 5 Helpers
- Adds extended utility classes and responsive tweaks.
### Table of Contents
- Select `toc-left` or `toc-right` in article **Options → Layout** to
insert TOC automatically.
### Dark Mode Toggle
- User-facing switch in the header.
- Remembers preference via local storage.
- Default behavior can be set in template settings.
### Soft Offline Mode
- Introduced in **2.1.5**.
- Allows articles in selected categories to remain viewable during
offline/maintenance mode.
- Useful for compliance, legal, or policy content.
## Development
For developers and contributors working on the moko-cassiopeia template:
### Quick Start for Developers
**New to the project?** See [Quick Start Guide](./docs/QUICK_START.md) for a 5-minute walkthrough.
### Development Resources
- **[Quick Start Guide](./docs/QUICK_START.md)** - Get up and running in 5 minutes
- **[Joomla Development Guide](./docs/JOOMLA_DEVELOPMENT.md)** - Testing, quality checks, and deployment
- **[Contributing Guide](./CONTRIBUTING.md)** - How to contribute
### Available Tools
- **Pre-commit Hooks**: Automatic validation before commits
## Changelog
See the [CHANGELOG.md](./CHANGELOG.md) for detailed version history.
MokoCassiopeia is a modern, lightweight enhancement layer built on top of Joomla's Cassiopeia template. It adds **Font Awesome 7**, **Bootstrap 5** helpers, an automatic **Table of Contents (TOC)** utility, advanced **Dark Mode** theming, and optional integrations for **Google Tag Manager** and **Google Analytics (GA4)**—all while maintaining minimal core template overrides for maximum upgrade compatibility.
---
## Metadata
## 📑 Table of Contents
* Maintainer: Moko Consulting Engineering
* Repository: [https://github.com/mokoconsulting-tech/moko-cassiopeia](https://github.com/mokoconsulting-tech/moko-cassiopeia)
* File: README.md
* Version: 3.0
* Classification: Public Open Source Standards
- [Features](#-features)
- [Requirements](#-requirements)
- [Installation](#-installation)
- [Quick Start](#-quick-start)
- [Configuration](#-configuration)
- [Theme System](#-theme-system)
- [Development](#-development)
- [Documentation](#-documentation)
- [Changelog](#-changelog)
- [Support](#-support)
- [Contributing](#-contributing)
- [Included Libraries](#-included-libraries)
- [License](#-license)
## Revision History
---
| Date | Change Summary | Author |
| ---------- | ------------------------------------------------------------------- | ------------------------------- |
| 2026-01-05 | Initial publication of template documentation and feature overview. | Moko Consulting |
| 2026-01-05 | Fixed malformed markdown table formatting in revision history. | Jonathan Miller (@jmiller-moko) |
## ✨ Features
### Core Enhancements
- **Built on Cassiopeia**: Extends Joomla's default template with minimal overrides
- **Font Awesome 7**: Fully integrated into Joomla's asset manager with 2,000+ icons
- **Bootstrap 5**: Extended utility classes and responsive grid system
- **No Template Overrides**: Clean installation that inherits all Cassiopeia defaults
- **Upgrade-Friendly**: Minimal modifications ensure smooth Joomla updates
### Advanced Theming
- **Dark Mode Support**: Built-in light/dark mode toggle with system preference detection
- **Color Palettes**: Standard, Alternative, and Custom color schemes
- **Theme Persistence**: User preferences saved via localStorage
- **Theme Control Options**: Switch, radio buttons, or hidden controls
- **Auto Dark Mode**: Optional automatic dark mode based on time/system settings
- **Meta Tags**: Automatic color-scheme and theme-color meta tags
### Developer Features
- **Custom Code Injection**: Add custom HTML to `<head>` start/end
- **Drawer Sidebars**: Configurable left/right drawer positions with custom icons
- **Font Options**: Local and web fonts (Roboto, Fira Sans, Noto Sans)
- **Sticky Header**: Optional sticky navigation
- **Back to Top**: Floating back-to-top button
### Analytics & Tracking
- **Google Tag Manager**: Optional GTM integration with container ID configuration
- **Google Analytics**: Optional GA4 integration with measurement ID
- **Privacy-Friendly**: All tracking features are optional and easily disabled
### Content Features
- **Table of Contents**: Automatic TOC generation for long articles
- Placement options: `toc-left` or `toc-right` layouts
- Automatic heading extraction and navigation
- Responsive sidebar positioning
---
## 📋 Requirements
- **Joomla**: 4.4.x or 5.x
- **PHP**: 8.0 or higher
- **Database**: MySQL 5.7+ / MariaDB 10.2+ / PostgreSQL 11+
- **Browser Support**: Modern browsers (Chrome, Firefox, Safari, Edge)
---
## 📦 Installation
### Via Joomla Extension Manager
1. Download the latest `mokocassiopeia-{version}.zip` from [Releases](https://github.com/mokoconsulting-tech/MokoCassiopeia/releases)
2. In Joomla admin, navigate to **System → Install → Extensions**
3. Upload the ZIP file and click **Upload & Install**
4. Navigate to **System → Site Templates**
5. Set **MokoCassiopeia** as your default template
### Via Git (Development)
```bash
git clone https://github.com/mokoconsulting-tech/MokoCassiopeia.git
cd MokoCassiopeia
```
See [Development Guide](./docs/JOOMLA_DEVELOPMENT.md) for development setup.
---
## 🚀 Quick Start
### 1. Install the Template
Install `mokocassiopeia.zip` via Joomla's Extension Manager.
### 2. Set as Default
Navigate to **System → Site Templates** and set **MokoCassiopeia** as default.
### 3. Configure Template Options
Go to **System → Site Templates → MokoCassiopeia** to configure:
- **Branding**: Upload logo, set site title/description
- **Theme**: Configure color schemes and dark mode
- **Layout**: Set container type (static/fluid), sticky header
- **Analytics**: Add GTM/GA4 tracking codes (optional)
- **Custom Code**: Inject custom HTML/CSS/JS
### 4. Test Dark Mode
The template includes a dark mode toggle. Test it by:
- Using the floating theme toggle button (bottom-right by default)
- Checking theme persistence across page loads
- Verifying system preference detection
---
## ⚙️ Configuration
### Global Parameters
Access template configuration via **System → Site Templates → MokoCassiopeia**.
#### Theme Tab
**General Settings:**
- **Theme Enabled**: Enable/disable theme system
- **Theme Control Type**: Switch (Light↔Dark), Radios (Light/Dark/System), or None
- **Default Choice**: System, Light, or Dark
- **Auto Dark Mode**: Automatic dark mode based on time
- **Meta Tags**: Enable color-scheme and theme-color meta tags
- **Bridge Bootstrap ARIA**: Sync theme with Bootstrap's data-bs-theme
**Variables & Palettes:**
- **Light Mode Palette**: Standard, Alternative, or Custom
- **Dark Mode Palette**: Standard, Alternative, or Custom
**Typography:**
- **Font Scheme**: Local (Roboto) or Web fonts (Fira Sans, Roboto+Noto Sans)
**Branding & Icons:**
- **Brand**: Enable/disable site branding
- **Logo File**: Upload custom logo (no default logo included)
- **Site Title**: Custom site title
- **Site Description**: Tagline/description
- **Font Awesome Kit**: Optional FA Pro kit code
**Header & Navigation:**
- **Sticky Header**: Enable fixed header on scroll
- **Back to Top**: Enable floating back-to-top button
**Theme Toggle UI:**
- **FAB Enabled**: Enable floating action button toggle
- **FAB Position**: Bottom-right, Bottom-left, Top-right, or Top-left
#### Advanced Tab
- **Layout**: Static or Fluid container
#### Google Tab
- **Google Tag Manager**: Enable and configure GTM container ID
- **Google Analytics**: Enable and configure GA4 measurement ID
#### Custom Code Tab
- **Custom Head Start**: HTML injected at start of `<head>`
- **Custom Head End**: HTML injected at end of `<head>`
#### Drawers Tab
- **Left Drawer Icon**: Font Awesome icon class (e.g., `fa-solid fa-chevron-right`)
- **Right Drawer Icon**: Font Awesome icon class (e.g., `fa-solid fa-chevron-left`)
### Custom Color Palettes
MokoCassiopeia supports custom color schemes:
1. Create `src/media/css/colors/light/colors_custom.css` for light mode
2. Create `src/media/css/colors/dark/colors_custom.css` for dark mode
3. Define CSS variables (reference `colors_standard.css` for structure)
4. Select "Custom" palette in template settings
**Example CSS Variables:**
```css
:root {
--cassiopeia-color-primary: #1e40af;
--cassiopeia-color-link: #2563eb;
--cassiopeia-color-hover: #1d4ed8;
--cassiopeia-color-text: #1f2937;
--cassiopeia-color-bg: #ffffff;
}
```
### Table of Contents
Enable automatic TOC for articles:
1. Edit an article in Joomla admin
2. Navigate to **Options → Layout**
3. Select **toc-left** or **toc-right**
4. Save the article
The TOC will automatically generate from article headings (H2, H3, etc.) and appear as a sidebar.
---
## 🎨 Theme System
### Dark Mode Features
- **Automatic Detection**: Respects user's system preferences
- **Manual Toggle**: Floating button or radio controls
- **Persistence**: Saves preference in localStorage
- **Smooth Transitions**: Animated theme switching
- **Comprehensive Support**: All components themed for dark mode
### Theme Control Types
1. **Switch**: Simple light/dark toggle button
2. **Radios**: Three options - Light, Dark, System
3. **None**: No visible control (respects system only)
### Meta Tags
When enabled, the template adds:
```html
<meta name="color-scheme" content="light dark">
<meta name="theme-color" content="#1e3a8a" media="(prefers-color-scheme: dark)">
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
```
---
## 🛠 Development
### For Contributors
**New to the project?** See [Quick Start Guide](./docs/QUICK_START.md) for a 5-minute setup.
### Development Resources
- **[Quick Start Guide](./docs/QUICK_START.md)** - Setup and first steps
- **[Joomla Development Guide](./docs/JOOMLA_DEVELOPMENT.md)** - Testing, quality checks, deployment
- **[Workflow Guide](./docs/WORKFLOW_GUIDE.md)** - Git workflow and branching
- **[Contributing Guide](./CONTRIBUTING.md)** - Contribution guidelines
- **[Roadmap](./docs/ROADMAP.md)** - Feature roadmap and planning
### Development Tools
- **Pre-commit Hooks**: Automatic validation before commits
- **PHP CodeSniffer**: Code style validation (Joomla standards)
- **PHPStan**: Static analysis for PHP code
- **Codeception**: Testing framework
### Quick Development Setup
```bash
# Clone repository
git clone https://github.com/mokoconsulting-tech/MokoCassiopeia.git
cd MokoCassiopeia
# Install development dependencies (if using Composer)
composer install --dev
# Run code quality checks
make validate # or manual commands
```
### Building Template Package
See [Joomla Development Guide](./docs/JOOMLA_DEVELOPMENT.md) for packaging instructions.
---
## 📚 Documentation
### User Documentation
- **[README](./README.md)** - This file (overview and features)
- **[CHANGELOG](./CHANGELOG.md)** - Version history and changes
- **[Roadmap](./docs/ROADMAP.md)** - Planned features and timeline
### Developer Documentation
- **[Quick Start](./docs/QUICK_START.md)** - 5-minute developer setup
- **[Development Guide](./docs/JOOMLA_DEVELOPMENT.md)** - Comprehensive development guide
- **[Workflow Guide](./docs/WORKFLOW_GUIDE.md)** - Git workflow and processes
- **[Documentation Index](./docs/README.md)** - All documentation links
### Governance
- **[Contributing](./CONTRIBUTING.md)** - How to contribute
- **[Code of Conduct](./CODE_OF_CONDUCT.md)** - Community standards
- **[Governance](./GOVERNANCE.md)** - Project governance model
- **[Security Policy](./SECURITY.md)** - Security reporting and procedures
---
## 📖 Changelog
See the [CHANGELOG.md](./CHANGELOG.md) for detailed version history.
### Recent Releases
- **[03.06.02]** (2026-01-30) - Complete rebrand to MokoCassiopeia, removed all overrides
- **[03.06.00]** (2026-01-28) - Version standardization
- **[03.05.01]** (2026-01-09) - Security and compliance improvements
- **[02.00.00]** (2025-08-30) - Dark mode toggle and improved theming
---
## 💬 Support
### Getting Help
- **Documentation**: Check this README and [docs folder](./docs/)
- **Issues**: Report bugs via [GitHub Issues](https://github.com/mokoconsulting-tech/MokoCassiopeia/issues)
- **Discussions**: Ask questions in [GitHub Discussions](https://github.com/mokoconsulting-tech/MokoCassiopeia/discussions)
- **Roadmap**: View planned features in [Roadmap](https://mokoconsulting.tech/support/joomla-cms/mokocassiopeia-roadmap)
### Reporting Bugs
Please include:
- Joomla version
- PHP version
- Template version
- Steps to reproduce
- Expected vs actual behavior
- Screenshots (if applicable)
### Security Issues
**Do not** report security vulnerabilities via public issues. See [SECURITY.md](./SECURITY.md) for reporting procedures.
---
## 🤝 Contributing
We welcome contributions! Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.
### How to Contribute
1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Make your changes
4. Run quality checks
5. Commit your changes (`git commit -m 'Add amazing feature'`)
6. Push to the branch (`git push origin feature/amazing-feature`)
7. Open a Pull Request
### Development Workflow
See [Workflow Guide](./docs/WORKFLOW_GUIDE.md) for detailed Git workflow.
---
## 📦 Included Libraries
MokoCassiopeia includes the following third-party libraries to provide enhanced functionality:
### Bootstrap TOC
- **Version**: 1.0.1
- **Author**: Aidan Feldman
- **License**: MIT License
- **Source**: [GitHub Repository](https://github.com/afeld/bootstrap-toc)
- **Release**: [v1.0.1 Release](https://github.com/afeld/bootstrap-toc/releases/tag/v1.0.1)
- **Purpose**: Automatically generates a table of contents from article headings with scrollspy support
- **Location**: `src/media/vendor/bootstrap-toc/`
- **Integration**: Registered in `joomla.asset.json` as `vendor.bootstrap-toc` (CSS) and `vendor.bootstrap-toc.js` (JavaScript)
- **Usage**: Activated when using `toc-left` or `toc-right` article layouts
- **Features**:
- Automatic TOC generation from H1-H6 headings
- Hierarchical nested navigation
- Active state highlighting with scrollspy
- Responsive design (collapses on mobile)
- Smooth scrolling to sections
- Automatic unique ID generation for headings
- **Customizations**: CSS adapted to use Cassiopeia CSS variables for theme compatibility
### Font Awesome 7 Free
- **Version**: 7.0 (Free)
- **License**: Font Awesome Free License
- **Source**: [Font Awesome](https://fontawesome.com)
- **Purpose**: Provides 2,000+ vector icons for interface elements
- **Location**: `src/media/vendor/fa7free/`
- **Integration**: Fully integrated into Joomla's asset manager
- **Styles Available**: Solid, Regular, Brands
### Bootstrap 5
- **Version**: 5.x (via Joomla)
- **License**: MIT License
- **Source**: [Bootstrap](https://getbootstrap.com)
- **Purpose**: Provides responsive grid system and utility classes
- **Integration**: Inherited from Joomla's Cassiopeia template, extended with additional helpers
- **Components Used**: Grid, utilities, modal, dropdown, collapse, offcanvas, tooltip, popover, scrollspy
### Integration Method
All third-party libraries are:
- ✅ Properly licensed and attributed
- ✅ Registered in Joomla's Web Asset Manager (`joomla.asset.json`)
- ✅ Loaded on-demand to optimize performance
- ✅ Versioned and documented for maintenance
- ✅ Compatible with Joomla 4.4.x and 5.x
---
## 📄 License
This project is licensed under the **GNU General Public License v3.0** - see the [LICENSE](./LICENSE) file for details.
### Third-Party Licenses
- **Joomla! CMS**: GPL-2.0-or-later
- **Cassiopeia Template**: GPL-2.0-or-later (Joomla Project)
- **Font Awesome 7 Free**: Font Awesome Free License
- **Bootstrap 5**: MIT License
- **Bootstrap TOC**: MIT License (A. Feld)
All third-party libraries and assets remain the property of their respective authors and are credited in source files.
---
## 🔗 Links
- **Repository**: [GitHub](https://github.com/mokoconsulting-tech/MokoCassiopeia)
- **Issue Tracker**: [GitHub Issues](https://github.com/mokoconsulting-tech/MokoCassiopeia/issues)
- **Discussions**: [GitHub Discussions](https://github.com/mokoconsulting-tech/MokoCassiopeia/discussions)
- **Roadmap**: [Full Roadmap](https://mokoconsulting.tech/support/joomla-cms/mokocassiopeia-roadmap)
- **Moko Consulting**: [Website](https://mokoconsulting.tech)
---
## 📊 Metadata
- **Maintainer**: Moko Consulting Engineering
- **Author**: Jonathan Miller (@jmiller-moko)
- **Repository**: https://github.com/mokoconsulting-tech/MokoCassiopeia
- **License**: GPL-3.0-or-later
- **Classification**: Public Open Source Standards
## 📝 Revision History
| Date | Version | Change Summary | Author |
| ---------- | -------- | ------------------------------------------------------------------------- | ------------------------------- |
| 2026-01-30 | 03.06.02 | Regenerated README with comprehensive documentation and updated structure | Copilot Agent |
| 2026-01-30 | 03.06.02 | Complete rebrand to MokoCassiopeia, removed overrides | Copilot Agent |
| 2026-01-05 | 03.00.00 | Initial publication of template documentation and feature overview | Moko Consulting |
| 2026-01-05 | 03.00.00 | Fixed malformed markdown table formatting in revision history | Jonathan Miller (@jmiller-moko) |
---
**Made with ❤️ by [Moko Consulting](https://mokoconsulting.tech)**

View File

@@ -7,18 +7,18 @@
# FILE INFORMATION
DEFGROUP: Joomla.Template
INGROUP: Moko-Cassiopeia.Governance
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
INGROUP: MokoCassiopeia.Governance
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
FILE: SECURITY.md
VERSION: 03.06.00
BRIEF: Security policy and vulnerability reporting process for Moko-Cassiopeia.
VERSION: 03.06.02
BRIEF: Security policy and vulnerability reporting process for MokoCassiopeia.
PATH: /SECURITY.md
NOTE: This policy is process oriented and does not replace secure engineering practices.
-->
## Security Policy
This document defines how Moko-Cassiopeia handles vulnerability intake, triage, remediation, and disclosure. The objective is to reduce risk, protect downstream users, and preserve operational continuity with a verifiable audit trail.
This document defines how MokoCassiopeia handles vulnerability intake, triage, remediation, and disclosure. The objective is to reduce risk, protect downstream users, and preserve operational continuity with a verifiable audit trail.
## Scope
@@ -47,7 +47,7 @@ Backports may be provided based on impact, deployment footprint, and engineering
Use one of the following channels:
* GitHub Security Advisories (preferred): use the repository security tab to submit a private report.
* Email: send details to `hello@mokoconsulting.tech` with subject `SECURITY: Moko-Cassiopeia vulnerability report`.
* Email: send details to `hello@mokoconsulting.tech` with subject `SECURITY: MokoCassiopeia vulnerability report`.
Do not file a public GitHub issue for suspected security vulnerabilities.
@@ -150,7 +150,7 @@ If you want credit, include the name or handle to list in an advisory. If you pr
## Metadata
* **Document:** SECURITY.md
* **Repository:** [https://github.com/mokoconsulting-tech/moko-cassiopeia](https://github.com/mokoconsulting-tech/moko-cassiopeia)
* **Repository:** [https://github.com/mokoconsulting-tech/MokoCassiopeia](https://github.com/mokoconsulting-tech/MokoCassiopeia)
* **Path:** /SECURITY.md
* **Owner:** Moko Consulting
* **Version:** 03.06.00

View File

@@ -1,6 +1,6 @@
# Quick Start Guide - Moko Cassiopeia Development
# Quick Start Guide - MokoCassiopeia Development
Get up and running with Moko Cassiopeia development in minutes.
Get up and running with MokoCassiopeia development in minutes.
## Prerequisites
@@ -17,8 +17,8 @@ Before you begin, ensure you have:
### 1. Clone the Repository
```bash
git clone https://github.com/mokoconsulting-tech/moko-cassiopeia.git
cd moko-cassiopeia
git clone https://github.com/mokoconsulting-tech/MokoCassiopeia.git
cd MokoCassiopeia
```
### 2. Install Development Dependencies
@@ -107,7 +107,7 @@ make package
# Check package contents
ls -lh dist/
unzip -l dist/moko-cassiopeia-*.zip
unzip -l dist/mokocassiopeia-*.zip
```
## VS Code Integration

View File

@@ -7,17 +7,17 @@
# FILE INFORMATION
DEFGROUP: Joomla.Template.Site
INGROUP: Moko-Cassiopeia.Documentation
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
INGROUP: MokoCassiopeia.Documentation
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
FILE: docs/README.md
VERSION: 03.06.00
BRIEF: Documentation index for Moko-Cassiopeia template
VERSION: 03.06.02
BRIEF: Documentation index for MokoCassiopeia template
PATH: /docs/README.md
-->
# Moko-Cassiopeia Documentation
# MokoCassiopeia Documentation
This directory contains comprehensive documentation for the Moko-Cassiopeia Joomla template.
This directory contains comprehensive documentation for the MokoCassiopeia Joomla template.
## Documentation Overview
@@ -87,8 +87,8 @@ This project adheres to [MokoStandards](https://github.com/mokoconsulting-tech/M
## Additional Resources
* **Repository**: [https://github.com/mokoconsulting-tech/moko-cassiopeia](https://github.com/mokoconsulting-tech/moko-cassiopeia)
* **Issue Tracker**: [GitHub Issues](https://github.com/mokoconsulting-tech/moko-cassiopeia/issues)
* **Repository**: [https://github.com/mokoconsulting-tech/MokoCassiopeia](https://github.com/mokoconsulting-tech/MokoCassiopeia)
* **Issue Tracker**: [GitHub Issues](https://github.com/mokoconsulting-tech/MokoCassiopeia/issues)
* **Changelog**: [CHANGELOG.md](../CHANGELOG.md)
* **License**: [GPL-3.0-or-later](../LICENSE)
@@ -102,10 +102,10 @@ This project adheres to [MokoStandards](https://github.com/mokoconsulting-tech/M
## Metadata
* Document: docs/README.md
* Repository: [https://github.com/mokoconsulting-tech/moko-cassiopeia](https://github.com/mokoconsulting-tech/moko-cassiopeia)
* Repository: [https://github.com/mokoconsulting-tech/MokoCassiopeia](https://github.com/mokoconsulting-tech/MokoCassiopeia)
* Path: /docs/README.md
* Owner: Moko Consulting
* Version: 03.06.00
* Version: 03.06.02
* Status: Active
* Effective Date: 2026-01-09

View File

@@ -7,24 +7,24 @@
# FILE INFORMATION
DEFGROUP: Joomla.Template.Site
INGROUP: Moko-Cassiopeia.Documentation
INGROUP: MokoCassiopeia.Documentation
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
FILE: docs/ROADMAP.md
VERSION: 03.06.00
BRIEF: Version-specific roadmap for Moko-Cassiopeia template
VERSION: 03.06.02
BRIEF: Version-specific roadmap for MokoCassiopeia template
PATH: /docs/ROADMAP.md
-->
# Moko-Cassiopeia Roadmap (VERSION: 03.06.00)
# MokoCassiopeia Roadmap (VERSION: 03.06.02)
This document provides a comprehensive, version-specific roadmap for the Moko-Cassiopeia Joomla template, tracking feature evolution, current capabilities, and planned enhancements.
This document provides a comprehensive, version-specific roadmap for the MokoCassiopeia Joomla template, tracking feature evolution, current capabilities, and planned enhancements.
## Table of Contents
- [Version Timeline](#version-timeline)
- [Past Releases](#past-releases)
- [Future Roadmap (5-Year Plan)](#future-roadmap-5-year-plan)
- [Current Release (v03.06.00)](#current-release-v030600)
- [Current Release (v03.06.02)](#current-release-v030600)
- [Implemented Features](#implemented-features)
- [Planned Features](#planned-features)
- [Development Priorities](#development-priorities)
@@ -437,7 +437,7 @@ The following versions represent our planned annual major releases, each buildin
---
## Current Release (v03.06.00)
## Current Release (v03.06.02)
### System Requirements
- **Joomla**: 4.4.x or 5.x
@@ -661,16 +661,63 @@ The following versions represent our planned annual major releases, each buildin
### 🚧 In Development
#### Soft Offline Mode (v2.1.5 - Mentioned)
**Status**: Planned/In Development
**Description**: Keep selected categories accessible during maintenance mode
#### 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
- Terms of service always available
**Configuration**:
- Admin-selectable categories
- Per-category offline access control
- 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)
@@ -715,11 +762,12 @@ The following versions represent our planned annual major releases, each buildin
## Development Priorities
### Immediate Focus (v03.x - 2026)
1. **TODO Tracking System**: Implement separate file for issue tracking
2. **Soft Offline Mode**: Complete category-based offline access
3. **Security Updates**: Maintain Dependabot and CodeQL scans
4. **Documentation**: Keep docs synchronized with features
5. **Bug Fixes**: Address reported issues and edge cases
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
copilot-pull-request-reviewer[bot] commented 2026-01-30 01:39:40 +00:00 (Migrated from github.com)
Review

The Soft Offline Mode specification mentions "Persistent Links Feature" with configuration paths like "System → Site Templates → MokoCassiopeia → Advanced → Offline Mode Settings". However, there are no corresponding field definitions added to templateDetails.xml in this PR. The roadmap describes this as "Planned for v03.07.00", but the PR title suggests this feature is being added now. Clarify whether this is documentation-only or if implementation is missing.

The Soft Offline Mode specification mentions "Persistent Links Feature" with configuration paths like "System → Site Templates → MokoCassiopeia → Advanced → Offline Mode Settings". However, there are no corresponding field definitions added to `templateDetails.xml` in this PR. The roadmap describes this as "Planned for v03.07.00", but the PR title suggests this feature is being added now. Clarify whether this is documentation-only or if implementation is missing.
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
@@ -750,7 +798,7 @@ The following versions represent our planned annual major releases, each buildin
## Long-term Vision
### Mission Statement
Moko-Cassiopeia aims to be the **most developer-friendly, user-customizable, and standards-compliant Joomla template** while maintaining minimal core overrides for maximum upgrade compatibility.
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
@@ -797,7 +845,7 @@ Moko-Cassiopeia aims to be the **most developer-friendly, user-customizable, and
## External Resources
### Official Links
- **Full Roadmap**: [https://mokoconsulting.tech/support/joomla-cms/moko-cassiopeia-roadmap](https://mokoconsulting.tech/support/joomla-cms/moko-cassiopeia-roadmap)
- **Full Roadmap**: [https://mokoconsulting.tech/support/joomla-cms/mokocassiopeia-roadmap](https://mokoconsulting.tech/support/joomla-cms/mokocassiopeia-roadmap)
- **Repository**: [https://github.com/mokoconsulting-tech/moko-cassiopeia](https://github.com/mokoconsulting-tech/moko-cassiopeia)
- **Issue Tracker**: [GitHub Issues](https://github.com/mokoconsulting-tech/moko-cassiopeia/issues)
- **Changelog**: [CHANGELOG.md](../CHANGELOG.md)
@@ -841,7 +889,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.06.00
* Version: 03.06.02
* Status: Active
* Last Updated: 2026-01-27
* Classification: Public Open Source Documentation

View File

@@ -1,6 +1,6 @@
<?xml version="1.0"?>
<ruleset name="Joomla Coding Standards">
<description>Joomla coding standards for Moko-Cassiopeia</description>
<description>Joomla coding standards for MokoCassiopeia</description>
<!-- Show progress and sniff names -->
<arg value="ps"/>

View File

@@ -1,35 +0,0 @@
; ; Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
;
; This file is part of a Moko Consulting project.
;
; SPDX-License-Identifier: GPL-3.0-or-later
;
; #FILE INFORMATION
; DEFGROUP: Joomla.Template.Site
; INGROUP: Moko-Cassiopeia
; PATH: ./language/en-GB/tpl_moko-cassiopeia.sys.ini
; VERSION: 03.05.00
; BRIEF: English (GB) system language strings for template metadata and installer
;
TPL_MOKO-CASSIOPEIA="Moko-Cassiopeia Site template"
TPL_MOKO-CASSIOPEIA_MOD_MENU_LAYOUT_COLLAPSE-METISMENU="Collapsible Dropdown"
TPL_MOKO-CASSIOPEIA_MOD_MENU_LAYOUT_DROPDOWN-METISMENU="Dropdown"
TPL_MOKO-CASSIOPEIA_POSITION_BANNER="Banner"
TPL_MOKO-CASSIOPEIA_POSITION_BELOW-TOP="Below Top"
TPL_MOKO-CASSIOPEIA_POSITION_BOTTOM-A="Bottom-A"
TPL_MOKO-CASSIOPEIA_POSITION_BOTTOM-B="Bottom-B"
TPL_MOKO-CASSIOPEIA_POSITION_BREADCRUMBS="Breadcrumbs"
TPL_MOKO-CASSIOPEIA_POSITION_DEBUG="Debug"
TPL_MOKO-CASSIOPEIA_POSITION_FOOTER="Footer"
TPL_MOKO-CASSIOPEIA_POSITION_MAIN-BOTTOM="Main-bottom"
TPL_MOKO-CASSIOPEIA_POSITION_MAIN-TOP="Main-top"
TPL_MOKO-CASSIOPEIA_POSITION_MENU="Menu"
TPL_MOKO-CASSIOPEIA_POSITION_SEARCH="Search"
TPL_MOKO-CASSIOPEIA_POSITION_SIDEBAR-LEFT="Sidebar-left"
TPL_MOKO-CASSIOPEIA_POSITION_SIDEBAR-RIGHT="Sidebar-right"
TPL_MOKO-CASSIOPEIA_POSITION_TOP-A="Top-a"
TPL_MOKO-CASSIOPEIA_POSITION_TOP-B="Top-b"
TPL_MOKO-CASSIOPEIA_POSITION_TOPBAR="Top Bar"
TPL_MOKO-CASSIOPEIA_POSITION_DRAWER-LEFT="Drawer-Left"
TPL_MOKO-CASSIOPEIA_POSITION_DRAWER-RIGHT="Drawer-Right"
TPL_MOKO-CASSIOPEIA_XML_DESCRIPTION="<h3>MOKO-CASSIOPEIA Template Description</h3> <p> <strong>MOKO-CASSIOPEIA 3.0</strong> continues Joomlas 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>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>"

View File

@@ -0,0 +1,35 @@
; ; Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
;
; This file is part of a Moko Consulting project.
;
; SPDX-License-Identifier: GPL-3.0-or-later
;
; #FILE INFORMATION
; DEFGROUP: Joomla.Template.Site
; INGROUP: MokoCassiopeia
; PATH: ./language/en-GB/tpl_mokocassiopeia.sys.ini
; VERSION: 03.06.02
; BRIEF: English (GB) system language strings for template metadata and installer
;
TPL_MOKOCASSIOPEIA="MokoCassiopeia Site template"
TPL_MOKOCASSIOPEIA_MOD_MENU_LAYOUT_COLLAPSE-METISMENU="Collapsible Dropdown"
TPL_MOKOCASSIOPEIA_MOD_MENU_LAYOUT_DROPDOWN-METISMENU="Dropdown"
TPL_MOKOCASSIOPEIA_POSITION_BANNER="Banner"
TPL_MOKOCASSIOPEIA_POSITION_BELOW-TOP="Below Top"
TPL_MOKOCASSIOPEIA_POSITION_BOTTOM-A="Bottom-A"
TPL_MOKOCASSIOPEIA_POSITION_BOTTOM-B="Bottom-B"
TPL_MOKOCASSIOPEIA_POSITION_BREADCRUMBS="Breadcrumbs"
TPL_MOKOCASSIOPEIA_POSITION_DEBUG="Debug"
TPL_MOKOCASSIOPEIA_POSITION_FOOTER="Footer"
TPL_MOKOCASSIOPEIA_POSITION_MAIN-BOTTOM="Main-bottom"
TPL_MOKOCASSIOPEIA_POSITION_MAIN-TOP="Main-top"
TPL_MOKOCASSIOPEIA_POSITION_MENU="Menu"
TPL_MOKOCASSIOPEIA_POSITION_SEARCH="Search"
TPL_MOKOCASSIOPEIA_POSITION_SIDEBAR-LEFT="Sidebar-left"
TPL_MOKOCASSIOPEIA_POSITION_SIDEBAR-RIGHT="Sidebar-right"
TPL_MOKOCASSIOPEIA_POSITION_TOP-A="Top-a"
TPL_MOKOCASSIOPEIA_POSITION_TOP-B="Top-b"
TPL_MOKOCASSIOPEIA_POSITION_TOPBAR="Top Bar"
TPL_MOKOCASSIOPEIA_POSITION_DRAWER-LEFT="Drawer-Left"
TPL_MOKOCASSIOPEIA_POSITION_DRAWER-RIGHT="Drawer-Right"
TPL_MOKOCASSIOPEIA_XML_DESCRIPTION="<h3>MokoCassiopeia Template Description</h3> <p> <strong>MokoCassiopeia 3.0</strong> continues Joomlas 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>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>"

View File

@@ -1,35 +0,0 @@
; ; Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
;
; This file is part of a Moko Consulting project.
;
; SPDX-License-Identifier: GPL-3.0-or-later
;
; #FILE INFORMATION
; DEFGROUP: Joomla.Template.Site
; INGROUP: Moko-Cassiopeia
; PATH: ./language/en-US/tpl_moko-cassiopeia.sys.ini
; VERSION: 03.05.00
; BRIEF: English (US) system language strings for template metadata and installer
;
TPL_MOKO-CASSIOPEIA="Moko-Cassiopeia Site template"
TPL_MOKO-CASSIOPEIA_MOD_MENU_LAYOUT_COLLAPSE-METISMENU="Collapsible Dropdown"
TPL_MOKO-CASSIOPEIA_MOD_MENU_LAYOUT_DROPDOWN-METISMENU="Dropdown"
TPL_MOKO-CASSIOPEIA_POSITION_BANNER="Banner"
TPL_MOKO-CASSIOPEIA_POSITION_BELOW-TOP="Below Top"
TPL_MOKO-CASSIOPEIA_POSITION_BOTTOM-A="Bottom-A"
TPL_MOKO-CASSIOPEIA_POSITION_BOTTOM-B="Bottom-B"
TPL_MOKO-CASSIOPEIA_POSITION_BREADCRUMBS="Breadcrumbs"
TPL_MOKO-CASSIOPEIA_POSITION_DEBUG="Debug"
TPL_MOKO-CASSIOPEIA_POSITION_FOOTER="Footer"
TPL_MOKO-CASSIOPEIA_POSITION_MAIN-BOTTOM="Main-bottom"
TPL_MOKO-CASSIOPEIA_POSITION_MAIN-TOP="Main-top"
TPL_MOKO-CASSIOPEIA_POSITION_MENU="Menu"
TPL_MOKO-CASSIOPEIA_POSITION_SEARCH="Search"
TPL_MOKO-CASSIOPEIA_POSITION_SIDEBAR-LEFT="Sidebar-left"
TPL_MOKO-CASSIOPEIA_POSITION_SIDEBAR-RIGHT="Sidebar-right"
TPL_MOKO-CASSIOPEIA_POSITION_TOP-A="Top-a"
TPL_MOKO-CASSIOPEIA_POSITION_TOP-B="Top-b"
TPL_MOKO-CASSIOPEIA_POSITION_TOPBAR="Top Bar"
TPL_MOKO-CASSIOPEIA_POSITION_DRAWER-LEFT="Drawer-Left"
TPL_MOKO-CASSIOPEIA_POSITION_DRAWER-RIGHT="Drawer-Right"
TPL_MOKO-CASSIOPEIA_XML_DESCRIPTION="<h3>MOKO-CASSIOPEIA Template Description</h3> <p> <strong>MOKO-CASSIOPEIA 3.0</strong> continues Joomlas 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>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>"

View File

@@ -0,0 +1,35 @@
; ; Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
;
; This file is part of a Moko Consulting project.
;
; SPDX-License-Identifier: GPL-3.0-or-later
;
; #FILE INFORMATION
; DEFGROUP: Joomla.Template.Site
; INGROUP: MokoCassiopeia
; PATH: ./language/en-US/tpl_mokocassiopeia.sys.ini
; VERSION: 03.06.02
; BRIEF: English (US) system language strings for template metadata and installer
;
TPL_MOKOCASSIOPEIA="MokoCassiopeia Site template"
TPL_MOKOCASSIOPEIA_MOD_MENU_LAYOUT_COLLAPSE-METISMENU="Collapsible Dropdown"
TPL_MOKOCASSIOPEIA_MOD_MENU_LAYOUT_DROPDOWN-METISMENU="Dropdown"
TPL_MOKOCASSIOPEIA_POSITION_BANNER="Banner"
TPL_MOKOCASSIOPEIA_POSITION_BELOW-TOP="Below Top"
TPL_MOKOCASSIOPEIA_POSITION_BOTTOM-A="Bottom-A"
TPL_MOKOCASSIOPEIA_POSITION_BOTTOM-B="Bottom-B"
TPL_MOKOCASSIOPEIA_POSITION_BREADCRUMBS="Breadcrumbs"
TPL_MOKOCASSIOPEIA_POSITION_DEBUG="Debug"
TPL_MOKOCASSIOPEIA_POSITION_FOOTER="Footer"
TPL_MOKOCASSIOPEIA_POSITION_MAIN-BOTTOM="Main-bottom"
TPL_MOKOCASSIOPEIA_POSITION_MAIN-TOP="Main-top"
TPL_MOKOCASSIOPEIA_POSITION_MENU="Menu"
TPL_MOKOCASSIOPEIA_POSITION_SEARCH="Search"
TPL_MOKOCASSIOPEIA_POSITION_SIDEBAR-LEFT="Sidebar-left"
TPL_MOKOCASSIOPEIA_POSITION_SIDEBAR-RIGHT="Sidebar-right"
TPL_MOKOCASSIOPEIA_POSITION_TOP-A="Top-a"
TPL_MOKOCASSIOPEIA_POSITION_TOP-B="Top-b"
TPL_MOKOCASSIOPEIA_POSITION_TOPBAR="Top Bar"
TPL_MOKOCASSIOPEIA_POSITION_DRAWER-LEFT="Drawer-Left"
TPL_MOKOCASSIOPEIA_POSITION_DRAWER-RIGHT="Drawer-Right"
TPL_MOKOCASSIOPEIA_XML_DESCRIPTION="<h3>MokoCassiopeia Template Description</h3> <p> <strong>MokoCassiopeia 3.0</strong> continues Joomlas 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>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>"

View File

@@ -1,116 +0,0 @@
; Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
;
; This file is part of a Moko Consulting project.
;
; SPDX-License-Identifier: GPL-3.0-or-later
;
; #FILE INFORMATION
; DEFGROUP: Joomla.Template.Site
; INGROUP: Moko-Cassiopeia
; PATH: ./language/en-GB/tpl_moko-cassiopeia.ini
; VERSION: 03.06.01
; BRIEF: English (GB) language strings for the Moko-Cassiopeia Joomla template
;
; ===== Template meta =====
MOKO-CASSIOPEIA="MOKO-CASSIOPEIA Site template"
TPL_MOKO-CASSIOPEIA_XML_DESCRIPTION="<h3>MOKO-CASSIOPEIA Template Description</h3> <p> <strong>MOKO-CASSIOPEIA 3.0</strong> continues Joomlas 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>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>"
; ===== System / layout =====
TPL_MOKO-CASSIOPEIA_DEVELOPMENTMODE_LABEL="Development Mode"
TPL_MOKO-CASSIOPEIA_DEVELOPMENTMODE_DESC="If 'Development Mode' is active, certain features may be disabled, such as Google Tag Manager and Google Analytics."
TPL_MOKO-CASSIOPEIA_FLUID_LABEL="Layout"
TPL_MOKO-CASSIOPEIA_STATIC="Static"
TPL_MOKO-CASSIOPEIA_FLUID="Fluid"
; ===== Custom Code tab =====
TPL_MOKO-CASSIOPEIA_CUSTOM_CODE_FIELDSET="Custom Code"
COM_TEMPLATES_CUSTOM_HEAD_FIELDSET_LABEL="Custom Head"
TPL_MOKO-CASSIOPEIA_CUSTOM_HEAD_START_LABEL="Custom Head: Start"
TPL_MOKO-CASSIOPEIA_CUSTOM_HEAD_START_DESC="This content will be inserted at the beginning of the &lt;head&gt; tag"
TPL_MOKO-CASSIOPEIA_CUSTOM_HEAD_END_LABEL="Custom Head: End"
TPL_MOKO-CASSIOPEIA_CUSTOM_HEAD_END_DESC="This content will be inserted at the end of the &lt;head&gt; tag"
TPL_MOKO-CASSIOPEIA_OFFLINEEMBED_LABEL="Offline Page Embed Code"
TPL_MOKO-CASSIOPEIA_OFFLINEEMBED_DESC="In addition to the 'Offline message' defined in 'Global Configuration', this will be displayed on the offline page.<i>Use for Mailchimp code and Social Icons</i>"
; ===== Drawers =====
COM_TEMPLATES_DRAWERS_FIELDSET_LABEL="Drawers"
TPL_MOKO-CASSIOPEIA_DRAWER_LEFT_ICON_LABEL="Drawer Left Icon CSS"
TPL_MOKO-CASSIOPEIA_DRAWER_LEFT_ICON_DESC="Enter the Font-Awesome class for the left drawer toggle (e.g. 'fas fa-chevron-left')."
TPL_MOKO-CASSIOPEIA_DRAWER_RIGHT_ICON_LABEL="Drawer Right Icon CSS"
TPL_MOKO-CASSIOPEIA_DRAWER_RIGHT_ICON_DESC="Enter the Font-Awesome class for the right drawer toggle (e.g. 'fas fa-chevron-right')."
; ===== Google =====
COM_TEMPLATES_GOOGLE_FIELDSET_LABEL="Google"
TPL_MOKO-CASSIOPEIA_GOOGLE_NOTE_TEXT="<h3>PLEASE NOTE:</h3>If fields are left blank, relative Google features will not be used"
TPL_MOKO-CASSIOPEIA_GOOGLETAGMANAGER_LABEL="Use Google Tag Manager?"
TPL_MOKO-CASSIOPEIA_GOOGLETAGMANAGER_DESC="Do you want to use Google Tag Manager?<br>More information on Google Tag Manager can be found <a target='_blank' href='https://support.google.com/tagmanager/answer/14842164'>here.</a>"
TPL_MOKO-CASSIOPEIA_GOOGLETAGMANAGERID_LABEL="Google Tag Manager ID"
TPL_MOKO-CASSIOPEIA_GOOGLETAGMANAGERID_DESC="Begins with 'GTM-'"
TPL_MOKO-CASSIOPEIA_GOOGLEANALYTICS_LABEL="Use Google Analytics?"
TPL_MOKO-CASSIOPEIA_GOOGLEANALYTICS_DESC="Do you want to use Google Analytics?<br>More information on Google Analytics can be found <a target='_blank' href='https://developers.google.com/analytics'>here.</a>"
TPL_MOKO-CASSIOPEIA_GOOGLEANALYTICSID_LABEL="Google Analytics ID"
TPL_MOKO-CASSIOPEIA_GOOGLEANALYTICSID_DESC="Begins with 'G-'"
; ===== Branding & icons (Theme tab) =====
TPL_MOKO-CASSIOPEIA_BRAND_LABEL="Brand"
TPL_MOKO-CASSIOPEIA_LOGO_LABEL="Logo"
TPL_MOKO-CASSIOPEIA_TITLE="Title (alternative to logo)"
TPL_MOKO-CASSIOPEIA_TAGLINE_LABEL="Tagline"
TPL_MOKO-CASSIOPEIA_TAGLINE_DESC="Optional text to show as a subheading"
TPL_MOKO-CASSIOPEIA_FA7KITCODE_LABEL="Font Awesome 7 Kit Unique Code"
TPL_MOKO-CASSIOPEIA_FA7KITCODE_DESC="<i>If left blank, Font Awesome 7 Free will be used.</i><br>Copy the unique Kit embed code above and paste it into the &lt;head&gt; of your project's HTML file or template.<br><a href='https://fontawesome.com/' target='_blank'>More information at the Font Awesome website.</a>"
; ===== Typography (Theme tab) =====
TPL_MOKO-CASSIOPEIA_FONT_LABEL="Fonts Scheme"
TPL_MOKO-CASSIOPEIA_FONT_GROUP_LOCAL="Fonts from Folder"
TPL_MOKO-CASSIOPEIA_FONT_GROUP_WEB="Fonts from Web"
TPL_MOKO-CASSIOPEIA_FONT_NOTE_TEXT="Loading fonts from external sources might be against privacy regulations in some countries.<br>Loading fonts from a local folder might have a performance impact on your site."
; ===== Header & navigation (Theme tab) =====
TPL_MOKO-CASSIOPEIA_STICKY_LABEL="Sticky Header"
TPL_MOKO-CASSIOPEIA_BACKTOTOP="Back to Top"
TPL_MOKO-CASSIOPEIA_BACKTOTOP_LABEL="Back-to-top Link"
TPL_MOKO-CASSIOPEIA_TOC="Table of Contents"
; ===== Color palette choices (shared) =====
TPL_MOKO-CASSIOPEIA_COLOR_NAME_STANDARD="Standard"
TPL_MOKO-CASSIOPEIA_COLOR_NAME_ALTERNATIVE="Alternative"
TPL_MOKO-CASSIOPEIA_COLOR_NAME_CUSTOM="Custom"
; New labels for Theme tab dropdowns
TPL_MOKO-CASSIOPEIA_COLOR_LIGHT_NAME_LABEL="Light color palette"
TPL_MOKO-CASSIOPEIA_COLOR_DARK_NAME_LABEL="Dark color palette"
; ===== Theme tab (core feature strings) =====
TPL_MOKO_THEME_FIELDSET="Theme"
TPL_MOKO_THEME_SECTION_GENERAL="General"
TPL_MOKO_THEME_SECTION_VARS="Variables & Palettes"
TPL_MOKO_THEME_SECTION_TYPO="Typography"
TPL_MOKO_THEME_SECTION_BRAND="Branding & Icons"
TPL_MOKO_THEME_SECTION_HEADER="Header & Navigation"
TPL_MOKO_THEME_SECTION_TOGGLE="Theme Toggle UI"
TPL_MOKO_THEME_ENABLED="Enable theme feature"
TPL_MOKO_THEME_ENABLED_DESC="Turn the entire light/dark feature on or off."
TPL_MOKO_THEME_CONTROL_TYPE="Theme Control Type"
TPL_MOKO_THEME_CONTROL_TYPE_DESC="Choose a visible toggle (Switch or Radios), or no control to follow System only."
TPL_MOKO_THEME_DEFAULT_CHOICE="Default Choice"
TPL_MOKO_THEME_DEFAULT_CHOICE_DESC="Initial theme when no user preference is stored."
TPL_MOKO_THEME_AUTO_DARK="Auto Dark Mode"
TPL_MOKO_THEME_AUTO_DARK_DESC="Force the site to switch to dark mode automatically. When enabled, the template will override the default and use dark unless the user explicitly selects otherwise."
TPL_MOKO_THEME_META_COLOR_SCHEME="Add &lt;meta name=&quot;color-scheme&quot;&gt;"
TPL_MOKO_THEME_META_COLOR_SCHEME_DESC="Advertise light/dark support for UA controls."
TPL_MOKO_THEME_META_THEME_COLOR="Add &lt;meta name=&quot;theme-color&quot;&gt;"
TPL_MOKO_THEME_META_THEME_COLOR_DESC="Update mobile address bar color."
TPL_MOKO_THEME_BASE_CSS="Base template CSS"
TPL_MOKO_THEME_BASE_CSS_DESC="Main stylesheet that consumes variables."
TPL_MOKO_THEME_BRIDGE="Sync data-bs-theme with data-aria-theme"
TPL_MOKO_THEME_BRIDGE_DESC="Keep both attributes in lockstep so Bootstrap and custom CSS stay aligned."
TPL_MOKO_THEME_FAB_ENABLED="Show floating theme switch"
TPL_MOKO_THEME_FAB_ENABLED_DESC="Display a persistent, accessible theme toggle."
TPL_MOKO_THEME_FAB_POS="Floating switch position"
TPL_MOKO_THEME_FAB_POS_DESC="Screen corner for the toggle."
; ===== Misc =====
MOD_BREADCRUMBS_HERE="YOU ARE HERE:"
JGLOBAL_OFFLINE="Offline"

View File

@@ -0,0 +1,116 @@
; Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
;
; This file is part of a Moko Consulting project.
;
; SPDX-License-Identifier: GPL-3.0-or-later
;
; #FILE INFORMATION
; DEFGROUP: Joomla.Template.Site
; INGROUP: MokoCassiopeia
; PATH: ./language/en-GB/tpl_mokocassiopeia.ini
; VERSION: 03.06.02
; BRIEF: English (GB) language strings for the MokoCassiopeia Joomla template
;
; ===== Template meta =====
MOKOCASSIOPEIA="MokoCassiopeia Site template"
TPL_MOKOCASSIOPEIA_XML_DESCRIPTION="<h3>MokoCassiopeia Template Description</h3> <p> <strong>MokoCassiopeia 3.0</strong> continues Joomlas 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>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>"
; ===== System / layout =====
TPL_MOKOCASSIOPEIA_DEVELOPMENTMODE_LABEL="Development Mode"
TPL_MOKOCASSIOPEIA_DEVELOPMENTMODE_DESC="If 'Development Mode' is active, certain features may be disabled, such as Google Tag Manager and Google Analytics."
TPL_MOKOCASSIOPEIA_FLUID_LABEL="Layout"
TPL_MOKOCASSIOPEIA_STATIC="Static"
TPL_MOKOCASSIOPEIA_FLUID="Fluid"
; ===== Custom Code tab =====
TPL_MOKOCASSIOPEIA_CUSTOM_CODE_FIELDSET="Custom Code"
COM_TEMPLATES_CUSTOM_HEAD_FIELDSET_LABEL="Custom Head"
TPL_MOKOCASSIOPEIA_CUSTOM_HEAD_START_LABEL="Custom Head: Start"
TPL_MOKOCASSIOPEIA_CUSTOM_HEAD_START_DESC="This content will be inserted at the beginning of the &lt;head&gt; tag"
TPL_MOKOCASSIOPEIA_CUSTOM_HEAD_END_LABEL="Custom Head: End"
TPL_MOKOCASSIOPEIA_CUSTOM_HEAD_END_DESC="This content will be inserted at the end of the &lt;head&gt; tag"
TPL_MOKOCASSIOPEIA_OFFLINEEMBED_LABEL="Offline Page Embed Code"
TPL_MOKOCASSIOPEIA_OFFLINEEMBED_DESC="In addition to the 'Offline message' defined in 'Global Configuration', this will be displayed on the offline page.<i>Use for Mailchimp code and Social Icons</i>"
; ===== Drawers =====
COM_TEMPLATES_DRAWERS_FIELDSET_LABEL="Drawers"
TPL_MOKOCASSIOPEIA_DRAWER_LEFT_ICON_LABEL="Drawer Left Icon CSS"
TPL_MOKOCASSIOPEIA_DRAWER_LEFT_ICON_DESC="Enter the Font-Awesome class for the left drawer toggle (e.g. 'fas fa-chevron-left')."
TPL_MOKOCASSIOPEIA_DRAWER_RIGHT_ICON_LABEL="Drawer Right Icon CSS"
TPL_MOKOCASSIOPEIA_DRAWER_RIGHT_ICON_DESC="Enter the Font-Awesome class for the right drawer toggle (e.g. 'fas fa-chevron-right')."
; ===== Google =====
COM_TEMPLATES_GOOGLE_FIELDSET_LABEL="Google"
TPL_MOKOCASSIOPEIA_GOOGLE_NOTE_TEXT="<h3>PLEASE NOTE:</h3>If fields are left blank, relative Google features will not be used"
TPL_MOKOCASSIOPEIA_GOOGLETAGMANAGER_LABEL="Use Google Tag Manager?"
TPL_MOKOCASSIOPEIA_GOOGLETAGMANAGER_DESC="Do you want to use Google Tag Manager?<br>More information on Google Tag Manager can be found <a target='_blank' href='https://support.google.com/tagmanager/answer/14842164'>here.</a>"
TPL_MOKOCASSIOPEIA_GOOGLETAGMANAGERID_LABEL="Google Tag Manager ID"
TPL_MOKOCASSIOPEIA_GOOGLETAGMANAGERID_DESC="Begins with 'GTM-'"
TPL_MOKOCASSIOPEIA_GOOGLEANALYTICS_LABEL="Use Google Analytics?"
TPL_MOKOCASSIOPEIA_GOOGLEANALYTICS_DESC="Do you want to use Google Analytics?<br>More information on Google Analytics can be found <a target='_blank' href='https://developers.google.com/analytics'>here.</a>"
TPL_MOKOCASSIOPEIA_GOOGLEANALYTICSID_LABEL="Google Analytics ID"
TPL_MOKOCASSIOPEIA_GOOGLEANALYTICSID_DESC="Begins with 'G-'"
; ===== Branding & icons (Theme tab) =====
TPL_MOKOCASSIOPEIA_BRAND_LABEL="Brand"
TPL_MOKOCASSIOPEIA_LOGO_LABEL="Logo"
TPL_MOKOCASSIOPEIA_TITLE="Title (alternative to logo)"
TPL_MOKOCASSIOPEIA_TAGLINE_LABEL="Tagline"
TPL_MOKOCASSIOPEIA_TAGLINE_DESC="Optional text to show as a subheading"
TPL_MOKOCASSIOPEIA_FA7KITCODE_LABEL="Font Awesome 7 Kit Unique Code"
TPL_MOKOCASSIOPEIA_FA7KITCODE_DESC="<i>If left blank, Font Awesome 7 Free will be used.</i><br>Copy the unique Kit embed code above and paste it into the &lt;head&gt; of your project's HTML file or template.<br><a href='https://fontawesome.com/' target='_blank'>More information at the Font Awesome website.</a>"
; ===== Typography (Theme tab) =====
TPL_MOKOCASSIOPEIA_FONT_LABEL="Fonts Scheme"
TPL_MOKOCASSIOPEIA_FONT_GROUP_LOCAL="Fonts from Folder"
TPL_MOKOCASSIOPEIA_FONT_GROUP_WEB="Fonts from Web"
TPL_MOKOCASSIOPEIA_FONT_NOTE_TEXT="Loading fonts from external sources might be against privacy regulations in some countries.<br>Loading fonts from a local folder might have a performance impact on your site."
; ===== Header & navigation (Theme tab) =====
TPL_MOKOCASSIOPEIA_STICKY_LABEL="Sticky Header"
TPL_MOKOCASSIOPEIA_BACKTOTOP="Back to Top"
TPL_MOKOCASSIOPEIA_BACKTOTOP_LABEL="Back-to-top Link"
TPL_MOKOCASSIOPEIA_TOC="Table of Contents"
; ===== Color palette choices (shared) =====
TPL_MOKOCASSIOPEIA_COLOR_NAME_STANDARD="Standard"
TPL_MOKOCASSIOPEIA_COLOR_NAME_ALTERNATIVE="Alternative"
TPL_MOKOCASSIOPEIA_COLOR_NAME_CUSTOM="Custom"
; New labels for Theme tab dropdowns
TPL_MOKOCASSIOPEIA_COLOR_LIGHT_NAME_LABEL="Light color palette"
TPL_MOKOCASSIOPEIA_COLOR_DARK_NAME_LABEL="Dark color palette"
; ===== Theme tab (core feature strings) =====
TPL_MOKO_THEME_FIELDSET="Theme"
TPL_MOKO_THEME_SECTION_GENERAL="General"
TPL_MOKO_THEME_SECTION_VARS="Variables & Palettes"
TPL_MOKO_THEME_SECTION_TYPO="Typography"
TPL_MOKO_THEME_SECTION_BRAND="Branding & Icons"
TPL_MOKO_THEME_SECTION_HEADER="Header & Navigation"
TPL_MOKO_THEME_SECTION_TOGGLE="Theme Toggle UI"
TPL_MOKO_THEME_ENABLED="Enable theme feature"
TPL_MOKO_THEME_ENABLED_DESC="Turn the entire light/dark feature on or off."
TPL_MOKO_THEME_CONTROL_TYPE="Theme Control Type"
TPL_MOKO_THEME_CONTROL_TYPE_DESC="Choose a visible toggle (Switch or Radios), or no control to follow System only."
TPL_MOKO_THEME_DEFAULT_CHOICE="Default Choice"
TPL_MOKO_THEME_DEFAULT_CHOICE_DESC="Initial theme when no user preference is stored."
TPL_MOKO_THEME_AUTO_DARK="Auto Dark Mode"
TPL_MOKO_THEME_AUTO_DARK_DESC="Force the site to switch to dark mode automatically. When enabled, the template will override the default and use dark unless the user explicitly selects otherwise."
TPL_MOKO_THEME_META_COLOR_SCHEME="Add &lt;meta name=&quot;color-scheme&quot;&gt;"
TPL_MOKO_THEME_META_COLOR_SCHEME_DESC="Advertise light/dark support for UA controls."
TPL_MOKO_THEME_META_THEME_COLOR="Add &lt;meta name=&quot;theme-color&quot;&gt;"
TPL_MOKO_THEME_META_THEME_COLOR_DESC="Update mobile address bar color."
TPL_MOKO_THEME_BASE_CSS="Base template CSS"
TPL_MOKO_THEME_BASE_CSS_DESC="Main stylesheet that consumes variables."
TPL_MOKO_THEME_BRIDGE="Sync data-bs-theme with data-aria-theme"
TPL_MOKO_THEME_BRIDGE_DESC="Keep both attributes in lockstep so Bootstrap and custom CSS stay aligned."
TPL_MOKO_THEME_FAB_ENABLED="Show floating theme switch"
TPL_MOKO_THEME_FAB_ENABLED_DESC="Display a persistent, accessible theme toggle."
TPL_MOKO_THEME_FAB_POS="Floating switch position"
TPL_MOKO_THEME_FAB_POS_DESC="Screen corner for the toggle."
; ===== Misc =====
MOD_BREADCRUMBS_HERE="YOU ARE HERE:"
JGLOBAL_OFFLINE="Offline"

View File

@@ -1,116 +0,0 @@
; ; Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
;
; This file is part of a Moko Consulting project.
;
; SPDX-License-Identifier: GPL-3.0-or-later
;
; #FILE INFORMATION
; DEFGROUP: Joomla.Template.Site
; INGROUP: Moko-Cassiopeia
; PATH: ./language/en-US/tpl_moko-cassiopeia.ini
; VERSION: 03.06.01
; BRIEF: English (US) language strings for the Moko-Cassiopeia Joomla template
;
; ===== Template meta =====
MOKO-CASSIOPEIA="MOKO-CASSIOPEIA Site template"
TPL_MOKO-CASSIOPEIA_XML_DESCRIPTION="<h3>MOKO-CASSIOPEIA Template Description</h3> <p> <strong>MOKO-CASSIOPEIA 3.0</strong> continues Joomlas 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>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>"
; ===== System / layout =====
TPL_MOKO-CASSIOPEIA_DEVELOPMENTMODE_LABEL="Development Mode"
TPL_MOKO-CASSIOPEIA_DEVELOPMENTMODE_DESC="If 'Development Mode' is active, certain features may be disabled, such as Google Tag Manager and Google Analytics."
TPL_MOKO-CASSIOPEIA_FLUID_LABEL="Layout"
TPL_MOKO-CASSIOPEIA_STATIC="Static"
TPL_MOKO-CASSIOPEIA_FLUID="Fluid"
; ===== Custom Code tab =====
TPL_MOKO-CASSIOPEIA_CUSTOM_CODE_FIELDSET="Custom Code"
COM_TEMPLATES_CUSTOM_HEAD_FIELDSET_LABEL="Custom Head"
TPL_MOKO-CASSIOPEIA_CUSTOM_HEAD_START_LABEL="Custom Head: Start"
TPL_MOKO-CASSIOPEIA_CUSTOM_HEAD_START_DESC="This content will be inserted at the beginning of the &lt;head&gt; tag"
TPL_MOKO-CASSIOPEIA_CUSTOM_HEAD_END_LABEL="Custom Head: End"
TPL_MOKO-CASSIOPEIA_CUSTOM_HEAD_END_DESC="This content will be inserted at the end of the &lt;head&gt; tag"
TPL_MOKO-CASSIOPEIA_OFFLINEEMBED_LABEL="Offline Page Embed Code"
TPL_MOKO-CASSIOPEIA_OFFLINEEMBED_DESC="In addition to the 'Offline message' defined in 'Global Configuration', this will be displayed on the offline page.<i>Use for Mailchimp code and Social Icons</i>"
; ===== Drawers =====
COM_TEMPLATES_DRAWERS_FIELDSET_LABEL="Drawers"
TPL_MOKO-CASSIOPEIA_DRAWER_LEFT_ICON_LABEL="Drawer Left Icon CSS"
TPL_MOKO-CASSIOPEIA_DRAWER_LEFT_ICON_DESC="Enter the Font-Awesome class for the left drawer toggle (e.g. 'fas fa-chevron-left')."
TPL_MOKO-CASSIOPEIA_DRAWER_RIGHT_ICON_LABEL="Drawer Right Icon CSS"
TPL_MOKO-CASSIOPEIA_DRAWER_RIGHT_ICON_DESC="Enter the Font-Awesome class for the right drawer toggle (e.g. 'fas fa-chevron-right')."
; ===== Google =====
COM_TEMPLATES_GOOGLE_FIELDSET_LABEL="Google"
TPL_MOKO-CASSIOPEIA_GOOGLE_NOTE_TEXT="<h3>PLEASE NOTE:</h3>If fields are left blank, relative Google features will not be used"
TPL_MOKO-CASSIOPEIA_GOOGLETAGMANAGER_LABEL="Use Google Tag Manager?"
TPL_MOKO-CASSIOPEIA_GOOGLETAGMANAGER_DESC="Do you want to use Google Tag Manager?<br>More information on Google Tag Manager can be found <a target='_blank' href='https://support.google.com/tagmanager/answer/14842164'>here.</a>"
TPL_MOKO-CASSIOPEIA_GOOGLETAGMANAGERID_LABEL="Google Tag Manager ID"
TPL_MOKO-CASSIOPEIA_GOOGLETAGMANAGERID_DESC="Begins with 'GTM-'"
TPL_MOKO-CASSIOPEIA_GOOGLEANALYTICS_LABEL="Use Google Analytics?"
TPL_MOKO-CASSIOPEIA_GOOGLEANALYTICS_DESC="Do you want to use Google Analytics?<br>More information on Google Analytics can be found <a target='_blank' href='https://developers.google.com/analytics'>here.</a>"
TPL_MOKO-CASSIOPEIA_GOOGLEANALYTICSID_LABEL="Google Analytics ID"
TPL_MOKO-CASSIOPEIA_GOOGLEANALYTICSID_DESC="Begins with 'G-'"
; ===== Branding & icons (Theme tab) =====
TPL_MOKO-CASSIOPEIA_BRAND_LABEL="Brand"
TPL_MOKO-CASSIOPEIA_LOGO_LABEL="Logo"
TPL_MOKO-CASSIOPEIA_TITLE="Title (alternative to logo)"
TPL_MOKO-CASSIOPEIA_TAGLINE_LABEL="Tagline"
TPL_MOKO-CASSIOPEIA_TAGLINE_DESC="Optional text to show as a subheading"
TPL_MOKO-CASSIOPEIA_FA7KITCODE_LABEL="Font Awesome 7 Kit Unique Code"
TPL_MOKO-CASSIOPEIA_FA7KITCODE_DESC="<i>If left blank, Font Awesome 7 Free will be used.</i><br>Copy the unique Kit embed code above and paste it into the &lt;head&gt; of your project's HTML file or template.<br><a href='https://fontawesome.com/' target='_blank'>More information at the Font Awesome website.</a>"
; ===== Typography (Theme tab) =====
TPL_MOKO-CASSIOPEIA_FONT_LABEL="Fonts Scheme"
TPL_MOKO-CASSIOPEIA_FONT_GROUP_LOCAL="Fonts from Folder"
TPL_MOKO-CASSIOPEIA_FONT_GROUP_WEB="Fonts from Web"
TPL_MOKO-CASSIOPEIA_FONT_NOTE_TEXT="Loading fonts from external sources might be against privacy regulations in some countries.<br>Loading fonts from a local folder might have a performance impact on your site."
; ===== Header & navigation (Theme tab) =====
TPL_MOKO-CASSIOPEIA_STICKY_LABEL="Sticky Header"
TPL_MOKO-CASSIOPEIA_BACKTOTOP="Back to Top"
TPL_MOKO-CASSIOPEIA_BACKTOTOP_LABEL="Back-to-top Link"
TPL_MOKO-CASSIOPEIA_TOC="Table of Contents"
; ===== Color palette choices (shared) =====
TPL_MOKO-CASSIOPEIA_COLOR_NAME_STANDARD="Standard"
TPL_MOKO-CASSIOPEIA_COLOR_NAME_ALTERNATIVE="Alternative"
TPL_MOKO-CASSIOPEIA_COLOR_NAME_CUSTOM="Custom"
; New labels for Theme tab dropdowns
TPL_MOKO-CASSIOPEIA_COLOR_LIGHT_NAME_LABEL="Light color palette"
TPL_MOKO-CASSIOPEIA_COLOR_DARK_NAME_LABEL="Dark color palette"
; ===== Theme tab (core feature strings) =====
TPL_MOKO_THEME_FIELDSET="Theme"
TPL_MOKO_THEME_SECTION_GENERAL="General"
TPL_MOKO_THEME_SECTION_VARS="Variables & Palettes"
TPL_MOKO_THEME_SECTION_TYPO="Typography"
TPL_MOKO_THEME_SECTION_BRAND="Branding & Icons"
TPL_MOKO_THEME_SECTION_HEADER="Header & Navigation"
TPL_MOKO_THEME_SECTION_TOGGLE="Theme Toggle UI"
TPL_MOKO_THEME_ENABLED="Enable theme feature"
TPL_MOKO_THEME_ENABLED_DESC="Turn the entire light/dark feature on or off."
TPL_MOKO_THEME_CONTROL_TYPE="Theme Control Type"
TPL_MOKO_THEME_CONTROL_TYPE_DESC="Choose a visible toggle (Switch or Radios), or no control to follow System only."
TPL_MOKO_THEME_DEFAULT_CHOICE="Default Choice"
TPL_MOKO_THEME_DEFAULT_CHOICE_DESC="Initial theme when no user preference is stored."
TPL_MOKO_THEME_AUTO_DARK="Auto Dark Mode"
TPL_MOKO_THEME_AUTO_DARK_DESC="Force the site to switch to dark mode automatically. When enabled, the template will override the default and use dark unless the user explicitly selects otherwise."
TPL_MOKO_THEME_META_COLOR_SCHEME="Add &lt;meta name=&quot;color-scheme&quot;&gt;"
TPL_MOKO_THEME_META_COLOR_SCHEME_DESC="Advertise light/dark support for UA controls."
TPL_MOKO_THEME_META_THEME_COLOR="Add &lt;meta name=&quot;theme-color&quot;&gt;"
TPL_MOKO_THEME_META_THEME_COLOR_DESC="Update mobile address bar color."
TPL_MOKO_THEME_BASE_CSS="Base template CSS"
TPL_MOKO_THEME_BASE_CSS_DESC="Main stylesheet that consumes variables."
TPL_MOKO_THEME_BRIDGE="Sync data-bs-theme with data-aria-theme"
TPL_MOKO_THEME_BRIDGE_DESC="Keep both attributes in lockstep so Bootstrap and custom CSS stay aligned."
TPL_MOKO_THEME_FAB_ENABLED="Show floating theme switch"
TPL_MOKO_THEME_FAB_ENABLED_DESC="Display a persistent, accessible theme toggle."
TPL_MOKO_THEME_FAB_POS="Floating switch position"
TPL_MOKO_THEME_FAB_POS_DESC="Screen corner for the toggle."
; ===== Misc =====
MOD_BREADCRUMBS_HERE="YOU ARE HERE:"
JGLOBAL_OFFLINE="Offline"

View File

@@ -0,0 +1,116 @@
; ; Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
;
; This file is part of a Moko Consulting project.
;
; SPDX-License-Identifier: GPL-3.0-or-later
;
; #FILE INFORMATION
; DEFGROUP: Joomla.Template.Site
; INGROUP: MokoCassiopeia
; PATH: ./language/en-US/tpl_mokocassiopeia.ini
; VERSION: 03.06.02
; BRIEF: English (US) language strings for the MokoCassiopeia Joomla template
;
; ===== Template meta =====
MOKOCASSIOPEIA="MOKOCASSIOPEIA Site template"
TPL_MOKOCASSIOPEIA_XML_DESCRIPTION="<h3>MOKOCASSIOPEIA Template Description</h3> <p> <strong>MOKOCASSIOPEIA 3.0</strong> continues Joomlas 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>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>"
; ===== System / layout =====
TPL_MOKOCASSIOPEIA_DEVELOPMENTMODE_LABEL="Development Mode"
TPL_MOKOCASSIOPEIA_DEVELOPMENTMODE_DESC="If 'Development Mode' is active, certain features may be disabled, such as Google Tag Manager and Google Analytics."
TPL_MOKOCASSIOPEIA_FLUID_LABEL="Layout"
TPL_MOKOCASSIOPEIA_STATIC="Static"
TPL_MOKOCASSIOPEIA_FLUID="Fluid"
; ===== Custom Code tab =====
TPL_MOKOCASSIOPEIA_CUSTOM_CODE_FIELDSET="Custom Code"
COM_TEMPLATES_CUSTOM_HEAD_FIELDSET_LABEL="Custom Head"
TPL_MOKOCASSIOPEIA_CUSTOM_HEAD_START_LABEL="Custom Head: Start"
TPL_MOKOCASSIOPEIA_CUSTOM_HEAD_START_DESC="This content will be inserted at the beginning of the &lt;head&gt; tag"
TPL_MOKOCASSIOPEIA_CUSTOM_HEAD_END_LABEL="Custom Head: End"
TPL_MOKOCASSIOPEIA_CUSTOM_HEAD_END_DESC="This content will be inserted at the end of the &lt;head&gt; tag"
TPL_MOKOCASSIOPEIA_OFFLINEEMBED_LABEL="Offline Page Embed Code"
TPL_MOKOCASSIOPEIA_OFFLINEEMBED_DESC="In addition to the 'Offline message' defined in 'Global Configuration', this will be displayed on the offline page.<i>Use for Mailchimp code and Social Icons</i>"
; ===== Drawers =====
COM_TEMPLATES_DRAWERS_FIELDSET_LABEL="Drawers"
TPL_MOKOCASSIOPEIA_DRAWER_LEFT_ICON_LABEL="Drawer Left Icon CSS"
TPL_MOKOCASSIOPEIA_DRAWER_LEFT_ICON_DESC="Enter the Font-Awesome class for the left drawer toggle (e.g. 'fas fa-chevron-left')."
TPL_MOKOCASSIOPEIA_DRAWER_RIGHT_ICON_LABEL="Drawer Right Icon CSS"
TPL_MOKOCASSIOPEIA_DRAWER_RIGHT_ICON_DESC="Enter the Font-Awesome class for the right drawer toggle (e.g. 'fas fa-chevron-right')."
; ===== Google =====
COM_TEMPLATES_GOOGLE_FIELDSET_LABEL="Google"
TPL_MOKOCASSIOPEIA_GOOGLE_NOTE_TEXT="<h3>PLEASE NOTE:</h3>If fields are left blank, relative Google features will not be used"
TPL_MOKOCASSIOPEIA_GOOGLETAGMANAGER_LABEL="Use Google Tag Manager?"
TPL_MOKOCASSIOPEIA_GOOGLETAGMANAGER_DESC="Do you want to use Google Tag Manager?<br>More information on Google Tag Manager can be found <a target='_blank' href='https://support.google.com/tagmanager/answer/14842164'>here.</a>"
TPL_MOKOCASSIOPEIA_GOOGLETAGMANAGERID_LABEL="Google Tag Manager ID"
TPL_MOKOCASSIOPEIA_GOOGLETAGMANAGERID_DESC="Begins with 'GTM-'"
TPL_MOKOCASSIOPEIA_GOOGLEANALYTICS_LABEL="Use Google Analytics?"
TPL_MOKOCASSIOPEIA_GOOGLEANALYTICS_DESC="Do you want to use Google Analytics?<br>More information on Google Analytics can be found <a target='_blank' href='https://developers.google.com/analytics'>here.</a>"
TPL_MOKOCASSIOPEIA_GOOGLEANALYTICSID_LABEL="Google Analytics ID"
TPL_MOKOCASSIOPEIA_GOOGLEANALYTICSID_DESC="Begins with 'G-'"
; ===== Branding & icons (Theme tab) =====
TPL_MOKOCASSIOPEIA_BRAND_LABEL="Brand"
TPL_MOKOCASSIOPEIA_LOGO_LABEL="Logo"
TPL_MOKOCASSIOPEIA_TITLE="Title (alternative to logo)"
TPL_MOKOCASSIOPEIA_TAGLINE_LABEL="Tagline"
TPL_MOKOCASSIOPEIA_TAGLINE_DESC="Optional text to show as a subheading"
TPL_MOKOCASSIOPEIA_FA7KITCODE_LABEL="Font Awesome 7 Kit Unique Code"
TPL_MOKOCASSIOPEIA_FA7KITCODE_DESC="<i>If left blank, Font Awesome 7 Free will be used.</i><br>Copy the unique Kit embed code above and paste it into the &lt;head&gt; of your project's HTML file or template.<br><a href='https://fontawesome.com/' target='_blank'>More information at the Font Awesome website.</a>"
; ===== Typography (Theme tab) =====
TPL_MOKOCASSIOPEIA_FONT_LABEL="Fonts Scheme"
TPL_MOKOCASSIOPEIA_FONT_GROUP_LOCAL="Fonts from Folder"
TPL_MOKOCASSIOPEIA_FONT_GROUP_WEB="Fonts from Web"
TPL_MOKOCASSIOPEIA_FONT_NOTE_TEXT="Loading fonts from external sources might be against privacy regulations in some countries.<br>Loading fonts from a local folder might have a performance impact on your site."
; ===== Header & navigation (Theme tab) =====
TPL_MOKOCASSIOPEIA_STICKY_LABEL="Sticky Header"
TPL_MOKOCASSIOPEIA_BACKTOTOP="Back to Top"
TPL_MOKOCASSIOPEIA_BACKTOTOP_LABEL="Back-to-top Link"
TPL_MOKOCASSIOPEIA_TOC="Table of Contents"
; ===== Color palette choices (shared) =====
TPL_MOKOCASSIOPEIA_COLOR_NAME_STANDARD="Standard"
TPL_MOKOCASSIOPEIA_COLOR_NAME_ALTERNATIVE="Alternative"
TPL_MOKOCASSIOPEIA_COLOR_NAME_CUSTOM="Custom"
; New labels for Theme tab dropdowns
TPL_MOKOCASSIOPEIA_COLOR_LIGHT_NAME_LABEL="Light color palette"
TPL_MOKOCASSIOPEIA_COLOR_DARK_NAME_LABEL="Dark color palette"
; ===== Theme tab (core feature strings) =====
TPL_MOKO_THEME_FIELDSET="Theme"
TPL_MOKO_THEME_SECTION_GENERAL="General"
TPL_MOKO_THEME_SECTION_VARS="Variables & Palettes"
TPL_MOKO_THEME_SECTION_TYPO="Typography"
TPL_MOKO_THEME_SECTION_BRAND="Branding & Icons"
TPL_MOKO_THEME_SECTION_HEADER="Header & Navigation"
TPL_MOKO_THEME_SECTION_TOGGLE="Theme Toggle UI"
TPL_MOKO_THEME_ENABLED="Enable theme feature"
TPL_MOKO_THEME_ENABLED_DESC="Turn the entire light/dark feature on or off."
TPL_MOKO_THEME_CONTROL_TYPE="Theme Control Type"
TPL_MOKO_THEME_CONTROL_TYPE_DESC="Choose a visible toggle (Switch or Radios), or no control to follow System only."
TPL_MOKO_THEME_DEFAULT_CHOICE="Default Choice"
TPL_MOKO_THEME_DEFAULT_CHOICE_DESC="Initial theme when no user preference is stored."
TPL_MOKO_THEME_AUTO_DARK="Auto Dark Mode"
TPL_MOKO_THEME_AUTO_DARK_DESC="Force the site to switch to dark mode automatically. When enabled, the template will override the default and use dark unless the user explicitly selects otherwise."
TPL_MOKO_THEME_META_COLOR_SCHEME="Add &lt;meta name=&quot;color-scheme&quot;&gt;"
TPL_MOKO_THEME_META_COLOR_SCHEME_DESC="Advertise light/dark support for UA controls."
TPL_MOKO_THEME_META_THEME_COLOR="Add &lt;meta name=&quot;theme-color&quot;&gt;"
TPL_MOKO_THEME_META_THEME_COLOR_DESC="Update mobile address bar color."
TPL_MOKO_THEME_BASE_CSS="Base template CSS"
TPL_MOKO_THEME_BASE_CSS_DESC="Main stylesheet that consumes variables."
TPL_MOKO_THEME_BRIDGE="Sync data-bs-theme with data-aria-theme"
TPL_MOKO_THEME_BRIDGE_DESC="Keep both attributes in lockstep so Bootstrap and custom CSS stay aligned."
TPL_MOKO_THEME_FAB_ENABLED="Show floating theme switch"
TPL_MOKO_THEME_FAB_ENABLED_DESC="Display a persistent, accessible theme toggle."
TPL_MOKO_THEME_FAB_POS="Floating switch position"
TPL_MOKO_THEME_FAB_POS_DESC="Screen corner for the toggle."
; ===== Misc =====
MOD_BREADCRUMBS_HERE="YOU ARE HERE:"
JGLOBAL_OFFLINE="Offline"

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 100 KiB

View File

@@ -0,0 +1,46 @@
/*!
* Bootstrap Table of Contents v1.0.1 (https://afeld.github.io/bootstrap-toc/)
* Copyright 2015 Aidan Feldman
* Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md)
*/
/* All levels of nav */
nav[data-toggle='toc'] .nav > li > a {
display: block;
padding: 4px 20px;
font-size: 13px;
font-weight: 500;
color: var(--cassiopeia-color-link, #0d6efd);
}
nav[data-toggle='toc'] .nav > li > a:hover,
nav[data-toggle='toc'] .nav > li > a:focus {
padding-left: 19px;
color: var(--cassiopeia-color-hover, #0a58ca);
text-decoration: none;
background-color: transparent;
border-left: 1px solid var(--cassiopeia-color-primary, #0d6efd);
}
nav[data-toggle='toc'] .nav > .active > a,
nav[data-toggle='toc'] .nav > .active:hover > a,
nav[data-toggle='toc'] .nav > .active:focus > a {
padding-left: 18px;
font-weight: bold;
color: var(--cassiopeia-color-primary, #0d6efd);
background-color: transparent;
border-left: 2px solid var(--cassiopeia-color-primary, #0d6efd);
}
/* Nav: second level (shown on .active) */
nav[data-toggle='toc'] .nav .nav {
display: none; /* Hide by default, but at >768px, show it */
padding-bottom: 10px;
}
nav[data-toggle='toc'] .nav > .active > ul {
display: block;
}
@media (min-width: 768px) {
nav[data-toggle='toc'] .nav .nav {
display: block;
}
}

View File

@@ -0,0 +1,170 @@
/*!
* Bootstrap Table of Contents v1.0.1 (https://afeld.github.io/bootstrap-toc/)
* Copyright 2015 Aidan Feldman
* Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md)
*/
(function($) {
'use strict';
window.Toc = {
helpers: {
// return all matching elements in the set, or their descendants
findOrFilter: function($el, selector) {
// http://danielnouri.org/notes/2011/03/14/a-jquery-find-that-also-finds-the-root-element/
// http://stackoverflow.com/a/12731439/358804
var $descendants = $el.find(selector);
return $el.filter(selector).add($descendants).filter(':not([data-toc-skip])');
},
generateUniqueIdBase: function(el) {
var text = $(el).text();
// adapted from
// https://github.com/bryanbraun/anchorjs/blob/5a7f01cbd56f8aa8413084a64e7d1cbb1d4b0e56/anchor.js#L237-L257
// and
// https://github.com/twbs/bootstrap/blob/b8a84c3e48ce62e5d1954c52d3796b3fcbeab8a9/site/docs/4.1/assets/js/src/application.js#L47-L53
// Remove punctuation and spaces
var base = text
.toLowerCase()
.replace(/[^\w\s-]/g, '')
.replace(/[\s]+/g, '-');
return base || el.tagName.toLowerCase();
},
generateUniqueId: function(el) {
var anchorBase = this.generateUniqueIdBase(el);
for (var i = 0; ; i++) {
var anchor = anchorBase;
if (i > 0) {
// add suffix
anchor += '-' + i;
}
// check if ID already exists
if (!document.getElementById(anchor)) {
return anchor;
}
}
},
generateAnchor: function(el) {
if (el.id) {
return el.id;
} else {
var anchor = this.generateUniqueId(el);
el.id = anchor;
return anchor;
}
},
createNavList: function() {
return $('<ul class="nav"></ul>');
},
createChildNavList: function($parent) {
var $childList = this.createNavList();
$parent.append($childList);
return $childList;
},
generateNavEl: function(anchor, text) {
var $a = $('<a class="nav-link"></a>');
$a.attr('href', '#' + anchor);
$a.text(text);
var $li = $('<li class="nav-item"></li>');
$li.append($a);
return $li;
},
generateNavItem: function(headingEl) {
var anchor = this.generateAnchor(headingEl);
var $heading = $(headingEl);
var text = $heading.data('toc-text') || $heading.text();
return this.generateNavEl(anchor, text);
},
// Find the first heading level (`<h1>`, then `<h2>`, etc.) that has more than one element. Defaults to 1 (for `<h1>`).
getTopLevel: function($scope) {
for (var i = 1; i <= 6; i++) {
var $headings = this.findOrFilter($scope, 'h' + i);
if ($headings.length > 1) {
return i;
}
}
return 1;
},
// returns the elements for the top level, and the next below it
getHeadings: function($scope, topLevel) {
var topSelector = 'h' + topLevel;
var secondaryLevel = topLevel + 1;
var secondarySelector = 'h' + secondaryLevel;
return this.findOrFilter($scope, topSelector + ',' + secondarySelector);
},
getNavLevel: function(el) {
return parseInt(el.tagName.charAt(1), 10);
},
populateNav: function($topContext, topLevel, $headings) {
var $context = $topContext;
var $prevNav;
var helpers = this;
$headings.each(function(i, el) {
var $newNav = helpers.generateNavItem(el);
var navLevel = helpers.getNavLevel(el);
// determine the proper $context
if (navLevel === topLevel) {
// use top level
$context = $topContext;
} else if ($prevNav && $context === $topContext) {
// create a new level of the tree and switch to it
$context = helpers.createChildNavList($prevNav);
} // else use the current $context
$context.append($newNav);
$prevNav = $newNav;
});
},
parseOps: function(arg) {
var opts;
if (arg.jquery) {
opts = {
$nav: arg
};
} else {
opts = arg;
}
opts.$scope = opts.$scope || $(document.body);
return opts;
}
},
// accepts a jQuery object, or an options object
init: function(opts) {
opts = this.helpers.parseOps(opts);
// ensure that the data attribute is in place for styling
opts.$nav.attr('data-toggle', 'toc');
var $topContext = this.helpers.createChildNavList(opts.$nav);
var topLevel = this.helpers.getTopLevel(opts.$scope);
var $headings = this.helpers.getHeadings(opts.$scope, topLevel);
this.helpers.populateNav($topContext, topLevel, $headings);
}
};
$(function() {
$('nav[data-toggle="toc"]').each(function(i, el) {
var $nav = $(el);
Toc.init($nav);
});
});
})(jQuery);

View File

@@ -0,0 +1 @@
<!DOCTYPE html><title></title>

View File

@@ -7,11 +7,11 @@
# FILE INFORMATION
DEFGROUP: Joomla.Template.Site
INGROUP: Moko-Cassiopeia
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
PATH: ./templates/moko-cassiopeia/component.php
VERSION: 03.06.00
BRIEF: Main template index file for Moko-Cassiopeia rendering site layout
INGROUP: MokoCassiopeia
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
PATH: ./templates/mokocassiopeia/component.php
VERSION: 03.06.02
BRIEF: Main template index file for MokoCassiopeia rendering site layout
*/
@@ -79,7 +79,7 @@ $final = $pageTitle !== ''
$this->setTitle($final);
// Template/Media path
$templatePath = 'media/templates/site/moko-cassiopeia';
$templatePath = 'media/templates/site/mokocassiopeia';
// Core template CSS
$wa->useStyle('template.base'); // css/template.css

View File

@@ -7,11 +7,11 @@
# FILE INFORMATION
DEFGROUP: Joomla.Template.Site
INGROUP: Moko-Cassiopeia
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
PATH: ./templates/moko-cassiopeia/custom.php
VERSION: 03.06.00
BRIEF: Custom entry template file for Moko-Cassiopeia with user-defined overrides
INGROUP: MokoCassiopeia
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
PATH: ./templates/mokocassiopeia/custom.php
VERSION: 03.06.02
BRIEF: MokoCassiopeia with user-defined overrides
*/
function console_log($output, $with_script_tags = true) {

View File

@@ -7,11 +7,11 @@
# FILE INFORMATION
DEFGROUP: Joomla.Template.Site
INGROUP: Moko-Cassiopeia
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
PATH: ./templates/moko-cassiopeia/error.php
VERSION: 03.06.00
BRIEF: Error page template file for Moko-Cassiopeia
INGROUP: MokoCassiopeia
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
PATH: ./templates/mokocassiopeia/error.php
VERSION: 03.06.02
BRIEF: Error page template file for MokoCassiopeia
*/
defined('_JEXEC') or die;
@@ -63,7 +63,7 @@ $params_leftIcon = htmlspecialchars($params->get('drawerLeftIcon', 'fa-solid f
$params_rightIcon = htmlspecialchars($params->get('drawerRightIcon', 'fa-solid fa-chevron-right'), ENT_QUOTES, 'UTF-8');
// Template/Media path
$templatePath = 'media/templates/site/moko-cassiopeia';
$templatePath = 'media/templates/site/mokocassiopeia';
// ===========================
// Web Asset Manager (WAM) — matches your joomla.asset.json
@@ -448,7 +448,7 @@ $debugOn = defined('JDEBUG') && JDEBUG;
</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'); ?>">
<a href="#top" id="back-top" class="back-to-top-link" aria-label="<?php echo Text::_('TPL_MOKOCASSIOPEIA_BACKTOTOP'); ?>">
<span class="icon-arrow-up icon-fw" aria-hidden="true"></span>
</a>
<?php endif; ?>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,177 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage com_contact
*
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Helper\ContentHelper;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\FileLayout;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Router\Route;
use Joomla\Component\Contact\Site\Helper\RouteHelper;
$tparams = $this->item->params;
$canDo = ContentHelper::getActions('com_contact', 'category', $this->item->catid);
$canEdit = $canDo->get('core.edit') || ($canDo->get('core.edit.own') && $this->item->created_by === Factory::getUser()->id);
$htag = $tparams->get('show_page_heading') ? 'h2' : 'h1';
?>
<div class="com-contact contact" itemscope itemtype="https://schema.org/Person">
<?php if ($canEdit) : ?>
<div class="icons">
<div class="text-end">
<div>
<?php echo HTMLHelper::_('contacticon.edit', $this->item, $tparams); ?>
</div>
</div>
</div>
<?php endif; ?>
<?php if ($tparams->get('show_page_heading')) : ?>
<h1>
<?php echo $this->escape($tparams->get('page_heading')); ?>
</h1>
<?php endif; ?>
<?php if ($this->item->name && $tparams->get('show_name')) : ?>
<div class="page-header">
<<?php echo $htag; ?>>
<?php if ($this->item->published == 0) : ?>
<span class="badge bg-warning text-light"><?php echo Text::_('JUNPUBLISHED'); ?></span>
<?php endif; ?>
<span class="contact-name" itemprop="name"><?php echo $this->item->name; ?></span>
</<?php echo $htag; ?>>
</div>
<?php endif; ?>
<div class="row gy-4 mb-4">
<div class="col-md-6">
<?php $show_contact_category = $tparams->get('show_contact_category'); ?>
<?php if ($show_contact_category === 'show_no_link') : ?>
<h3>
<span class="contact-category"><?php echo $this->item->category_title; ?></span>
</h3>
<?php elseif ($show_contact_category === 'show_with_link') : ?>
<?php $contactLink = RouteHelper::getCategoryRoute($this->item->catid, $this->item->language); ?>
<h3>
<span class="contact-category"><a href="<?php echo $contactLink; ?>">
<?php echo $this->escape($this->item->category_title); ?></a>
</span>
</h3>
<?php endif; ?>
<?php echo $this->item->event->afterDisplayTitle; ?>
<?php if ($tparams->get('show_contact_list') && count($this->contacts) > 1) : ?>
<form action="#" method="get" name="selectForm" id="selectForm">
<label for="select_contact"><?php echo Text::_('COM_CONTACT_SELECT_CONTACT'); ?></label>
<?php echo HTMLHelper::_(
'select.genericlist',
$this->contacts,
'select_contact',
'class="form-select" onchange="document.location.href = this.value"',
'link',
'name',
$this->item->link
);
?>
</form>
<?php endif; ?>
<?php if ($tparams->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
<div class="com-contact__tags">
<?php $this->item->tagLayout = new FileLayout('joomla.content.tags'); ?>
<?php echo $this->item->tagLayout->render($this->item->tags->itemTags); ?>
</div>
<?php endif; ?>
<?php echo $this->item->event->beforeDisplayContent; ?>
<?php if ($this->params->get('show_info', 1)) : ?>
<div class="com-contact__container">
<?php echo '<h3>' . Text::_('COM_CONTACT_DETAILS') . '</h3>'; ?>
<?php if ($this->item->image && $tparams->get('show_image')) : ?>
<div class="com-contact__thumbnail thumbnail">
<?php echo LayoutHelper::render(
'joomla.html.image',
[
'src' => $this->item->image,
'alt' => $this->item->name,
'itemprop' => 'image',
]
); ?>
</div>
<?php endif; ?>
<?php if ($this->item->con_position && $tparams->get('show_position')) : ?>
<dl class="com-contact__position contact-position dl-horizontal">
<dt><?php echo Text::_('COM_CONTACT_POSITION'); ?>:</dt>
<dd itemprop="jobTitle">
<?php echo $this->item->con_position; ?>
</dd>
</dl>
<?php endif; ?>
<div class="com-contact__info">
<?php echo $this->loadTemplate('address'); ?>
<?php if ($tparams->get('allow_vcard')) : ?>
<?php echo Text::_('COM_CONTACT_DOWNLOAD_INFORMATION_AS'); ?>
<a href="<?php echo Route::_('index.php?option=com_contact&view=contact&catid=' . $this->item->catslug . '&id=' . $this->item->slug . '&format=vcf'); ?>">
<?php echo Text::_('COM_CONTACT_VCARD'); ?></a>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<?php if ($tparams->get('show_links')) : ?>
<?php echo $this->loadTemplate('links'); ?>
<?php endif; ?>
<?php if ($tparams->get('show_articles') && $this->item->user_id && $this->item->articles) : ?>
<?php echo '<h3>' . Text::_('JGLOBAL_ARTICLES') . '</h3>'; ?>
<?php echo $this->loadTemplate('articles'); ?>
<?php endif; ?>
<?php if ($tparams->get('show_profile') && $this->item->user_id && PluginHelper::isEnabled('user', 'profile')) : ?>
<?php echo '<h3>' . Text::_('COM_CONTACT_PROFILE') . '</h3>'; ?>
<?php echo $this->loadTemplate('profile'); ?>
<?php endif; ?>
<?php if ($tparams->get('show_user_custom_fields') && $this->contactUser) : ?>
<?php echo $this->loadTemplate('user_custom_fields'); ?>
<?php endif; ?>
</div>
<div class="col-md-6">
<?php if ($tparams->get('show_email_form') && ($this->item->email_to || $this->item->user_id)) : ?>
<?php echo $this->loadTemplate('form'); ?>
<?php endif; ?>
</div>
</div>
<?php if ($this->item->misc && $tparams->get('show_misc')) : ?>
<?php echo '<h3>' . Text::_('COM_CONTACT_OTHER_INFORMATION') . '</h3>'; ?>
<div class="com-contact__miscinfo contact-miscinfo">
<div class="contact-misc">
<?php echo $this->item->misc; ?>
</div>
</div>
<?php endif; ?>
<?php echo $this->item->event->afterDisplayContent; ?>
</div>

View File

@@ -1,81 +0,0 @@
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
This file is part of a Moko Consulting project.
SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
DEFGROUP: Joomla.Templates.Site
INGROUP: Moko-Cassiopeia
FILE: index.html
BRIEF: Security redirect page to block folder access and forward to site root.
-->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Redirecting…</title>
<!-- Search engines: do not index this placeholder redirect page -->
<meta name="robots" content="noindex, nofollow, noarchive" />
<!-- Instant redirect fallback even if JavaScript is disabled -->
<meta http-equiv="refresh" content="0; url=/" />
<!-- Canonical root reference -->
<link rel="canonical" href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
(function redirectToRoot() {
// Configuration object with safe defaults.
var opts = {
fallbackPath: "/", // string: fallback destination if origin is unavailable
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
behavior: "replace" // enum: "replace" | "assign"
};
// Determine absolute origin in all mainstream browsers.
var origin = (typeof location.origin === "string" && location.origin)
|| (location.protocol + "//" + location.host);
// Final destination: absolute root of the current site, or fallback path.
var destination = origin ? origin + "/" : opts.fallbackPath;
function go() {
if (opts.behavior === "assign") {
location.assign(destination);
} else {
location.replace(destination);
}
}
// Execute redirect, optionally after a short delay.
if (opts.delayMs > 0) {
setTimeout(go, opts.delayMs);
} else {
go();
}
})();
</script>
<!--
Secondary meta-refresh for no-JS environments is already set above.
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
-->
<noscript>
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
<style>
html, body { height:100%; }
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
.msg { opacity: .75; text-align: center; }
</style>
</noscript>
</head>
<body>
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
</body>
</html>

View File

@@ -1,177 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage com_contact
*
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Helper\ContentHelper;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\FileLayout;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Router\Route;
use Joomla\Component\Contact\Site\Helper\RouteHelper;
$tparams = $this->item->params;
$canDo = ContentHelper::getActions('com_contact', 'category', $this->item->catid);
$canEdit = $canDo->get('core.edit') || ($canDo->get('core.edit.own') && $this->item->created_by === Factory::getUser()->id);
$htag = $tparams->get('show_page_heading') ? 'h2' : 'h1';
?>
<div class="com-contact contact" itemscope itemtype="https://schema.org/Person">
<?php if ($canEdit) : ?>
<div class="icons">
<div class="text-end">
<div>
<?php echo HTMLHelper::_('contacticon.edit', $this->item, $tparams); ?>
</div>
</div>
</div>
<?php endif; ?>
<?php if ($tparams->get('show_page_heading')) : ?>
<h1>
<?php echo $this->escape($tparams->get('page_heading')); ?>
</h1>
<?php endif; ?>
<?php if ($this->item->name && $tparams->get('show_name')) : ?>
<div class="page-header">
<<?php echo $htag; ?>>
<?php if ($this->item->published == 0) : ?>
<span class="badge bg-warning text-light"><?php echo Text::_('JUNPUBLISHED'); ?></span>
<?php endif; ?>
<span class="contact-name" itemprop="name"><?php echo $this->item->name; ?></span>
</<?php echo $htag; ?>>
</div>
<?php endif; ?>
<div class="row gy-4 mb-4">
<div class="col-md-6">
<?php $show_contact_category = $tparams->get('show_contact_category'); ?>
<?php if ($show_contact_category === 'show_no_link') : ?>
<h3>
<span class="contact-category"><?php echo $this->item->category_title; ?></span>
</h3>
<?php elseif ($show_contact_category === 'show_with_link') : ?>
<?php $contactLink = RouteHelper::getCategoryRoute($this->item->catid, $this->item->language); ?>
<h3>
<span class="contact-category"><a href="<?php echo $contactLink; ?>">
<?php echo $this->escape($this->item->category_title); ?></a>
</span>
</h3>
<?php endif; ?>
<?php echo $this->item->event->afterDisplayTitle; ?>
<?php if ($tparams->get('show_contact_list') && count($this->contacts) > 1) : ?>
<form action="#" method="get" name="selectForm" id="selectForm">
<label for="select_contact"><?php echo Text::_('COM_CONTACT_SELECT_CONTACT'); ?></label>
<?php echo HTMLHelper::_(
'select.genericlist',
$this->contacts,
'select_contact',
'class="form-select" onchange="document.location.href = this.value"',
'link',
'name',
$this->item->link
);
?>
</form>
<?php endif; ?>
<?php if ($tparams->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
<div class="com-contact__tags">
<?php $this->item->tagLayout = new FileLayout('joomla.content.tags'); ?>
<?php echo $this->item->tagLayout->render($this->item->tags->itemTags); ?>
</div>
<?php endif; ?>
<?php echo $this->item->event->beforeDisplayContent; ?>
<?php if ($this->params->get('show_info', 1)) : ?>
<div class="com-contact__container">
<?php echo '<h3>' . Text::_('COM_CONTACT_DETAILS') . '</h3>'; ?>
<?php if ($this->item->image && $tparams->get('show_image')) : ?>
<div class="com-contact__thumbnail thumbnail">
<?php echo LayoutHelper::render(
'joomla.html.image',
[
'src' => $this->item->image,
'alt' => $this->item->name,
'itemprop' => 'image',
]
); ?>
</div>
<?php endif; ?>
<?php if ($this->item->con_position && $tparams->get('show_position')) : ?>
<dl class="com-contact__position contact-position dl-horizontal">
<dt><?php echo Text::_('COM_CONTACT_POSITION'); ?>:</dt>
<dd itemprop="jobTitle">
<?php echo $this->item->con_position; ?>
</dd>
</dl>
<?php endif; ?>
<div class="com-contact__info">
<?php echo $this->loadTemplate('address'); ?>
<?php if ($tparams->get('allow_vcard')) : ?>
<?php echo Text::_('COM_CONTACT_DOWNLOAD_INFORMATION_AS'); ?>
<a href="<?php echo Route::_('index.php?option=com_contact&view=contact&catid=' . $this->item->catslug . '&id=' . $this->item->slug . '&format=vcf'); ?>">
<?php echo Text::_('COM_CONTACT_VCARD'); ?></a>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<?php if ($tparams->get('show_links')) : ?>
<?php echo $this->loadTemplate('links'); ?>
<?php endif; ?>
<?php if ($tparams->get('show_articles') && $this->item->user_id && $this->item->articles) : ?>
<?php echo '<h3>' . Text::_('JGLOBAL_ARTICLES') . '</h3>'; ?>
<?php echo $this->loadTemplate('articles'); ?>
<?php endif; ?>
<?php if ($tparams->get('show_profile') && $this->item->user_id && PluginHelper::isEnabled('user', 'profile')) : ?>
<?php echo '<h3>' . Text::_('COM_CONTACT_PROFILE') . '</h3>'; ?>
<?php echo $this->loadTemplate('profile'); ?>
<?php endif; ?>
<?php if ($tparams->get('show_user_custom_fields') && $this->contactUser) : ?>
<?php echo $this->loadTemplate('user_custom_fields'); ?>
<?php endif; ?>
</div>
<div class="col-md-6">
<?php if ($tparams->get('show_email_form') && ($this->item->email_to || $this->item->user_id)) : ?>
<?php echo $this->loadTemplate('form'); ?>
<?php endif; ?>
</div>
</div>
<?php if ($this->item->misc && $tparams->get('show_misc')) : ?>
<?php echo '<h3>' . Text::_('COM_CONTACT_OTHER_INFORMATION') . '</h3>'; ?>
<div class="com-contact__miscinfo contact-miscinfo">
<div class="contact-misc">
<?php echo $this->item->misc; ?>
</div>
</div>
<?php endif; ?>
<?php echo $this->item->event->afterDisplayContent; ?>
</div>

View File

@@ -1,81 +0,0 @@
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
This file is part of a Moko Consulting project.
SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
DEFGROUP: Joomla.Templates.Site
INGROUP: Moko-Cassiopeia
FILE: index.html
BRIEF: Security redirect page to block folder access and forward to site root.
-->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Redirecting…</title>
<!-- Search engines: do not index this placeholder redirect page -->
<meta name="robots" content="noindex, nofollow, noarchive" />
<!-- Instant redirect fallback even if JavaScript is disabled -->
<meta http-equiv="refresh" content="0; url=/" />
<!-- Canonical root reference -->
<link rel="canonical" href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
(function redirectToRoot() {
// Configuration object with safe defaults.
var opts = {
fallbackPath: "/", // string: fallback destination if origin is unavailable
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
behavior: "replace" // enum: "replace" | "assign"
};
// Determine absolute origin in all mainstream browsers.
var origin = (typeof location.origin === "string" && location.origin)
|| (location.protocol + "//" + location.host);
// Final destination: absolute root of the current site, or fallback path.
var destination = origin ? origin + "/" : opts.fallbackPath;
function go() {
if (opts.behavior === "assign") {
location.assign(destination);
} else {
location.replace(destination);
}
}
// Execute redirect, optionally after a short delay.
if (opts.delayMs > 0) {
setTimeout(go, opts.delayMs);
} else {
go();
}
})();
</script>
<!--
Secondary meta-refresh for no-JS environments is already set above.
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
-->
<noscript>
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
<style>
html, body { height:100%; }
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
.msg { opacity: .75; text-align: center; }
</style>
</noscript>
</head>
<body>
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
</body>
</html>

View File

@@ -1,81 +1 @@
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
This file is part of a Moko Consulting project.
SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
DEFGROUP: Joomla.Templates.Site
INGROUP: Moko-Cassiopeia
FILE: index.html
BRIEF: Security redirect page to block folder access and forward to site root.
-->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Redirecting…</title>
<!-- Search engines: do not index this placeholder redirect page -->
<meta name="robots" content="noindex, nofollow, noarchive" />
<!-- Instant redirect fallback even if JavaScript is disabled -->
<meta http-equiv="refresh" content="0; url=/" />
<!-- Canonical root reference -->
<link rel="canonical" href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
(function redirectToRoot() {
// Configuration object with safe defaults.
var opts = {
fallbackPath: "/", // string: fallback destination if origin is unavailable
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
behavior: "replace" // enum: "replace" | "assign"
};
// Determine absolute origin in all mainstream browsers.
var origin = (typeof location.origin === "string" && location.origin)
|| (location.protocol + "//" + location.host);
// Final destination: absolute root of the current site, or fallback path.
var destination = origin ? origin + "/" : opts.fallbackPath;
function go() {
if (opts.behavior === "assign") {
location.assign(destination);
} else {
location.replace(destination);
}
}
// Execute redirect, optionally after a short delay.
if (opts.delayMs > 0) {
setTimeout(go, opts.delayMs);
} else {
go();
}
})();
</script>
<!--
Secondary meta-refresh for no-JS environments is already set above.
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
-->
<noscript>
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
<style>
html, body { height:100%; }
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
.msg { opacity: .75; text-align: center; }
</style>
</noscript>
</head>
<body>
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
</body>
</html>
<!DOCTYPE html><title></title>

View File

@@ -1,17 +1,17 @@
<?php
/* Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
This file is part of a Moko Consulting project.
SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
DEFGROUP: Joomla.Template.Site
INGROUP: Moko-Cassiopeia
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
PATH: ./templates/moko-cassiopeia/html/com_content/article/toc-left.php
VERSION: 03.06.00
BRIEF: Template override for Joomla articles with Table of Contents aligned left
/**
* @package Joomla.Site
* @subpackage Templates.MokoCassiopeia
*
* @copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
* @license GNU General Public License version 3 or later; see LICENSE.txt
*
* FILE INFORMATION
* DEFGROUP: Joomla.Template.Site
* INGROUP: MokoCassiopeia
* PATH: ./templates/mokocassiopeia/html/com_content/article/toc-left.php
* VERSION: 03.06.02
* BRIEF: Article layout with table of contents on the left side using Bootstrap TOC
*/
defined('_JEXEC') or die;
@@ -19,141 +19,123 @@ defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Associations;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\FileLayout;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Uri\Uri;
use Joomla\Component\Content\Administrator\Extension\ContentComponent;
use Joomla\Component\Content\Site\Helper\RouteHelper;
// Create shortcuts to some parameters.
$params = $this->item->params;
// Load Bootstrap TOC assets
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();
$wa->useStyle('vendor.bootstrap-toc');
$wa->useScript('vendor.bootstrap-toc.js');
// Get article params
$params = $this->item->params;
$images = json_decode($this->item->images);
$urls = json_decode($this->item->urls);
$canEdit = $params->get('access-edit');
$user = Factory::getUser();
$info = $params->get('info_block_position', 0);
$htag = $this->params->get('show_page_heading') ? 'h2' : 'h1';
$info = $params->get('info_block_position', 0);
// Check if associations are implemented. If they are, define the parameter.
$assocParam = (Associations::isEnabled() && $params->get('show_associations'));
$currentDate = Factory::getDate()->format('Y-m-d H:i:s');
$isNotPublishedYet = $this->item->publish_up > $currentDate;
$isExpired = !is_null($this->item->publish_down) && $this->item->publish_down < $currentDate;
// Check if associations are implemented
$assocParam = (Associations::isEnabled() && $params->get('show_associations'));
?>
<div class="com-content-article item-page<?php echo $this->pageclass_sfx; ?>" itemscope itemtype="https://schema.org/Article">
<meta itemprop="inLanguage" content="<?php echo ($this->item->language === '*') ? Factory::getApplication()->get('language') : $this->item->language; ?>">
<?php if ($this->params->get('show_page_heading')) : ?>
<div class="page-header">
<h1> <?php echo $this->escape($this->params->get('page_heading')); ?> </h1>
</div>
<?php endif;
if (!empty($this->item->pagination) && !$this->item->paginationposition && $this->item->paginationrelative) {
echo $this->item->pagination;
}
?>
<?php $useDefList = $params->get('show_modify_date') || $params->get('show_publish_date') || $params->get('show_create_date')
|| $params->get('show_hits') || $params->get('show_category') || $params->get('show_parent_category') || $params->get('show_author') || $assocParam; ?>
<div class="com-content-article item-page<?php echo $this->pageclass_sfx; ?>">
<div class="row">
<!-- Table of Contents - Left Side -->
<div class="col-lg-3 col-md-4 order-md-1 mb-4">
<div class="sticky-top toc-wrapper" style="top: 20px;">
<nav id="toc" data-toggle="toc" class="toc-container">
<h5 class="toc-title"><?php echo HTMLHelper::_('string.truncate', $this->item->title, 50); ?></h5>
</nav>
</div>
</div>
<?php if ($params->get('show_title')) : ?>
<div class="page-header">
<<?php echo $htag; ?> itemprop="headline">
<?php echo $this->escape($this->item->title); ?>
</<?php echo $htag; ?>>
<?php if ($this->item->state == ContentComponent::CONDITION_UNPUBLISHED) : ?>
<span class="badge bg-warning text-light"><?php echo Text::_('JUNPUBLISHED'); ?></span>
<?php endif; ?>
<?php if ($isNotPublishedYet) : ?>
<span class="badge bg-warning text-light"><?php echo Text::_('JNOTPUBLISHEDYET'); ?></span>
<?php endif; ?>
<?php if ($isExpired) : ?>
<span class="badge bg-warning text-light"><?php echo Text::_('JEXPIRED'); ?></span>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if ($canEdit) : ?>
<?php echo LayoutHelper::render('joomla.content.icons', ['params' => $params, 'item' => $this->item]); ?>
<?php endif; ?>
<!-- Article Content -->
<div class="col-lg-9 col-md-8 order-md-2" data-toc-scope>
<meta itemprop="inLanguage" content="<?php echo ($this->item->language === '*') ? Factory::getApplication()->get('language') : $this->item->language; ?>" />
<?php // Content is generated by content plugin event "onContentAfterTitle" ?>
<?php echo $this->item->event->afterDisplayTitle; ?>
<?php if ($this->params->get('show_page_heading')) : ?>
<div class="page-header">
<h1><?php echo $this->escape($this->params->get('page_heading')); ?></h1>
</div>
<?php endif; ?>
<?php if ($useDefList && ($info == 0 || $info == 2)) : ?>
<?php echo LayoutHelper::render('joomla.content.info_block', ['item' => $this->item, 'params' => $params, 'position' => 'above']); ?>
<?php endif; ?>
<?php if (!$this->print) : ?>
<?php if ($canEdit || $params->get('show_print_icon') || $params->get('show_email_icon')) : ?>
<?php echo LayoutHelper::render('joomla.content.icons', ['params' => $params, 'item' => $this->item, 'print' => false]); ?>
<?php endif; ?>
<?php else : ?>
<?php if ($params->get('show_print_icon')) : ?>
<?php echo LayoutHelper::render('joomla.content.icons', ['params' => $params, 'item' => $this->item, 'print' => true]); ?>
<?php endif; ?>
<?php endif; ?>
<?php if ($info == 0 && $params->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
<?php $this->item->tagLayout = new FileLayout('joomla.content.tags'); ?>
<?php echo $this->item->event->afterDisplayTitle; ?>
<?php echo $this->item->tagLayout->render($this->item->tags->itemTags); ?>
<?php endif; ?>
<?php if ($params->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
<?php echo LayoutHelper::render('joomla.content.tags', $this->item->tags->itemTags); ?>
<?php endif; ?>
<?php // Content is generated by content plugin event "onContentBeforeDisplay" ?>
<?php echo $this->item->event->beforeDisplayContent; ?>
<?php echo $this->item->event->beforeDisplayContent; ?>
<?php if ((int) $params->get('urls_position', 0) === 0) : ?>
<?php echo $this->loadTemplate('links'); ?>
<?php endif; ?>
<?php if ($params->get('access-view')) : ?>
<?php echo LayoutHelper::render('joomla.content.full_image', $this->item); ?>
<?php
if (!empty($this->item->pagination) && !$this->item->paginationposition && !$this->item->paginationrelative) :
echo $this->item->pagination;
endif;
?>
<div itemprop="articleBody" class="com-content-article__body">
<div class="container-toc-left">
<?php
// Table of Contents header using template language string
echo '<h2>' . Text::_('TPL_MOKO-CASSIOPEIA_TOC') . '</h2>';
?>
<nav id="toc" data-toggle="toc"></nav>
</div>
<?php
echo $this->item->text;
?>
<?php if (isset($urls) && ((!empty($urls->urls_position) && $urls->urls_position == '0') || ($params->get('urls_position') == '0' && empty($urls->urls_position))) || (empty($urls->urls_position) && (!$params->get('urls_position')))) : ?>
<?php echo $this->loadTemplate('links'); ?>
<?php endif; ?>
<?php if ($params->get('access-view')) : ?>
<?php echo LayoutHelper::render('joomla.content.full_image', $this->item); ?>
<?php if (isset($info) && $info == 0 && $params->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
<?php echo LayoutHelper::render('joomla.content.info_block', ['item' => $this->item, 'params' => $params, 'position' => 'above']); ?>
<?php endif; ?>
<?php if ($info == 0 && $params->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
<?php echo LayoutHelper::render('joomla.content.tags', $this->item->tags->itemTags); ?>
<?php endif; ?>
<div class="article-content" itemprop="articleBody">
<?php echo $this->item->text; ?>
</div>
<?php if (isset($urls) && ((!empty($urls->urls_position) && $urls->urls_position == '1') || ($params->get('urls_position') == '1'))) : ?>
<?php echo $this->loadTemplate('links'); ?>
<?php endif; ?>
<?php elseif ($params->get('show_noauth') == true && $this->user->get('guest')) : ?>
<?php echo LayoutHelper::render('joomla.content.intro_image', $this->item); ?>
<?php echo HTMLHelper::_('content.prepare', $this->item->introtext); ?>
<?php endif; ?>
<?php echo $this->item->event->afterDisplayContent; ?>
<?php if (isset($info) && ($info == 1 || $info == 2)) : ?>
<?php if ($params->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
<?php echo LayoutHelper::render('joomla.content.tags', $this->item->tags->itemTags); ?>
<?php endif; ?>
<?php echo LayoutHelper::render('joomla.content.info_block', ['item' => $this->item, 'params' => $params, 'position' => 'below']); ?>
<?php endif; ?>
</div>
</div>
</div>
<style>
.toc-container {
background: var(--cassiopeia-color-bg, #fff);
border: 1px solid var(--cassiopeia-color-border, #dee2e6);
border-radius: 0.375rem;
padding: 1rem;
}
<?php if ($info == 1 || $info == 2) : ?>
<?php if ($useDefList) : ?>
<?php echo LayoutHelper::render('joomla.content.info_block', ['item' => $this->item, 'params' => $params, 'position' => 'below']); ?>
<?php endif; ?>
<?php if ($params->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
<?php $this->item->tagLayout = new FileLayout('joomla.content.tags'); ?>
<?php echo $this->item->tagLayout->render($this->item->tags->itemTags); ?>
<?php endif; ?>
<?php endif; ?>
<?php
if (!empty($this->item->pagination) && $this->item->paginationposition && !$this->item->paginationrelative) :
echo $this->item->pagination;
?>
<?php endif; ?>
<?php if ((int) $params->get('urls_position', 0) === 1) : ?>
<?php echo $this->loadTemplate('links'); ?>
<?php endif; ?>
<?php // Optional teaser intro text for guests ?>
<?php elseif ($params->get('show_noauth') == true && $user->get('guest')) : ?>
<?php echo LayoutHelper::render('joomla.content.intro_image', $this->item); ?>
<?php echo HTMLHelper::_('content.prepare', $this->item->introtext); ?>
<?php // Optional link to let them register to see the whole article. ?>
<?php if ($params->get('show_readmore') && $this->item->fulltext != null) : ?>
<?php $menu = Factory::getApplication()->getMenu(); ?>
<?php $active = $menu->getActive(); ?>
<?php $itemId = $active->id; ?>
<?php $link = new Uri(Route::_('index.php?option=com_users&view=login&Itemid=' . $itemId, false)); ?>
<?php $link->setVar('return', base64_encode(RouteHelper::getArticleRoute($this->item->slug, $this->item->catid, $this->item->language))); ?>
<?php echo LayoutHelper::render('joomla.content.readmore', ['item' => $this->item, 'params' => $params, 'link' => $link]); ?>
<?php endif; ?>
<?php endif; ?>
<?php
if (!empty($this->item->pagination) && $this->item->paginationposition && $this->item->paginationrelative) :
echo $this->item->pagination;
?>
<?php endif; ?>
<?php // Content is generated by content plugin event "onContentAfterDisplay" ?>
<?php echo $this->item->event->afterDisplayContent; ?>
</div>
.toc-title {
margin-bottom: 0.75rem;
font-size: 1rem;
font-weight: 600;
color: var(--cassiopeia-color-text, #212529);
border-bottom: 1px solid var(--cassiopeia-color-border, #dee2e6);
padding-bottom: 0.5rem;
}
@media (max-width: 767.98px) {
.toc-wrapper {
position: static !important;
margin-bottom: 1.5rem;
}
}
</style>

View File

@@ -1,17 +1,17 @@
<?php
/* Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
This file is part of a Moko Consulting project.
SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
DEFGROUP: Joomla.Template.Site
INGROUP: Moko-Cassiopeia
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
PATH: ./templates/moko-cassiopeia/html/com_content/article/toc-right.php
VERSION: 03.06.00
BRIEF: Template override for Joomla articles with Table of Contents aligned right
/**
* @package Joomla.Site
* @subpackage Templates.MokoCassiopeia
*
* @copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
* @license GNU General Public License version 3 or later; see LICENSE.txt
*
* FILE INFORMATION
* DEFGROUP: Joomla.Template.Site
* INGROUP: MokoCassiopeia
* PATH: ./templates/mokocassiopeia/html/com_content/article/toc-right.php
* VERSION: 03.06.02
* BRIEF: Article layout with table of contents on the right side using Bootstrap TOC
*/
defined('_JEXEC') or die;
@@ -19,143 +19,123 @@ defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Associations;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\FileLayout;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Uri\Uri;
use Joomla\Component\Content\Administrator\Extension\ContentComponent;
use Joomla\Component\Content\Site\Helper\RouteHelper;
// Create shortcuts to some parameters.
$params = $this->item->params;
// Load Bootstrap TOC assets
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();
$wa->useStyle('vendor.bootstrap-toc');
$wa->useScript('vendor.bootstrap-toc.js');
// Get article params
$params = $this->item->params;
$images = json_decode($this->item->images);
$urls = json_decode($this->item->urls);
$canEdit = $params->get('access-edit');
$user = Factory::getUser();
$info = $params->get('info_block_position', 0);
$htag = $this->params->get('show_page_heading') ? 'h2' : 'h1';
$info = $params->get('info_block_position', 0);
// Check if associations are implemented. If they are, define the parameter.
$assocParam = (Associations::isEnabled() && $params->get('show_associations'));
$currentDate = Factory::getDate()->format('Y-m-d H:i:s');
$isNotPublishedYet = $this->item->publish_up > $currentDate;
$isExpired = !is_null($this->item->publish_down) && $this->item->publish_down < $currentDate;
// Check if associations are implemented
$assocParam = (Associations::isEnabled() && $params->get('show_associations'));
?>
<div class="com-content-article item-page<?php echo $this->pageclass_sfx; ?>" itemscope itemtype="https://schema.org/Article">
<meta itemprop="inLanguage" content="<?php echo ($this->item->language === '*') ? Factory::getApplication()->get('language') : $this->item->language; ?>">
<?php if ($this->params->get('show_page_heading')) : ?>
<div class="page-header">
<h1> <?php echo $this->escape($this->params->get('page_heading')); ?> </h1>
</div>
<?php endif;
if (!empty($this->item->pagination) && !$this->item->paginationposition && $this->item->paginationrelative) {
echo $this->item->pagination;
}
?>
<?php $useDefList = $params->get('show_modify_date') || $params->get('show_publish_date') || $params->get('show_create_date')
|| $params->get('show_hits') || $params->get('show_category') || $params->get('show_parent_category') || $params->get('show_author') || $assocParam; ?>
<div class="com-content-article item-page<?php echo $this->pageclass_sfx; ?>">
<div class="row">
<!-- Article Content -->
<div class="col-lg-9 col-md-8 order-md-1" data-toc-scope>
<meta itemprop="inLanguage" content="<?php echo ($this->item->language === '*') ? Factory::getApplication()->get('language') : $this->item->language; ?>" />
<?php if ($params->get('show_title')) : ?>
<div class="page-header">
<<?php echo $htag; ?> itemprop="headline">
<?php echo $this->escape($this->item->title); ?>
</<?php echo $htag; ?>>
<?php if ($this->item->state == ContentComponent::CONDITION_UNPUBLISHED) : ?>
<span class="badge bg-warning text-light"><?php echo Text::_('JUNPUBLISHED'); ?></span>
<?php endif; ?>
<?php if ($isNotPublishedYet) : ?>
<span class="badge bg-warning text-light"><?php echo Text::_('JNOTPUBLISHEDYET'); ?></span>
<?php endif; ?>
<?php if ($isExpired) : ?>
<span class="badge bg-warning text-light"><?php echo Text::_('JEXPIRED'); ?></span>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if ($canEdit) : ?>
<?php echo LayoutHelper::render('joomla.content.icons', ['params' => $params, 'item' => $this->item]); ?>
<?php endif; ?>
<?php if ($this->params->get('show_page_heading')) : ?>
<div class="page-header">
<h1><?php echo $this->escape($this->params->get('page_heading')); ?></h1>
</div>
<?php endif; ?>
<?php // Content is generated by content plugin event "onContentAfterTitle" ?>
<?php echo $this->item->event->afterDisplayTitle; ?>
<?php if (!$this->print) : ?>
<?php if ($canEdit || $params->get('show_print_icon') || $params->get('show_email_icon')) : ?>
<?php echo LayoutHelper::render('joomla.content.icons', ['params' => $params, 'item' => $this->item, 'print' => false]); ?>
<?php endif; ?>
<?php else : ?>
<?php if ($params->get('show_print_icon')) : ?>
<?php echo LayoutHelper::render('joomla.content.icons', ['params' => $params, 'item' => $this->item, 'print' => true]); ?>
<?php endif; ?>
<?php endif; ?>
<?php if ($useDefList && ($info == 0 || $info == 2)) : ?>
<?php echo LayoutHelper::render('joomla.content.info_block', ['item' => $this->item, 'params' => $params, 'position' => 'above']); ?>
<?php endif; ?>
<?php echo $this->item->event->afterDisplayTitle; ?>
<?php if ($info == 0 && $params->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
<?php $this->item->tagLayout = new FileLayout('joomla.content.tags'); ?>
<?php if ($params->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
<?php echo LayoutHelper::render('joomla.content.tags', $this->item->tags->itemTags); ?>
<?php endif; ?>
<?php echo $this->item->tagLayout->render($this->item->tags->itemTags); ?>
<?php endif; ?>
<?php echo $this->item->event->beforeDisplayContent; ?>
<?php // Content is generated by content plugin event "onContentBeforeDisplay" ?>
<?php echo $this->item->event->beforeDisplayContent; ?>
<?php if (isset($urls) && ((!empty($urls->urls_position) && $urls->urls_position == '0') || ($params->get('urls_position') == '0' && empty($urls->urls_position))) || (empty($urls->urls_position) && (!$params->get('urls_position')))) : ?>
<?php echo $this->loadTemplate('links'); ?>
<?php endif; ?>
<?php if ((int) $params->get('urls_position', 0) === 0) : ?>
<?php echo $this->loadTemplate('links'); ?>
<?php endif; ?>
<?php if ($params->get('access-view')) : ?>
<?php echo LayoutHelper::render('joomla.content.full_image', $this->item); ?>
<?php
if (!empty($this->item->pagination) && !$this->item->paginationposition && !$this->item->paginationrelative) :
echo $this->item->pagination;
endif;
?>
<div itemprop="articleBody" class="com-content-article__body">
<div class="container-toc-right">
<?php
// Table of Contents header using template language string
<?php if ($params->get('access-view')) : ?>
<?php echo LayoutHelper::render('joomla.content.full_image', $this->item); ?>
echo '<h2>' . Text::_('TPL_MOKO-CASSIOPEIA_TOC') . '</h2>';
?>
<?php if (isset($info) && $info == 0 && $params->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
<?php echo LayoutHelper::render('joomla.content.info_block', ['item' => $this->item, 'params' => $params, 'position' => 'above']); ?>
<?php endif; ?>
<nav id="toc" data-toggle="toc"></nav>
</div>
<?php
echo $this->item->text;
?>
<?php if ($info == 0 && $params->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
<?php echo LayoutHelper::render('joomla.content.tags', $this->item->tags->itemTags); ?>
<?php endif; ?>
<div class="article-content" itemprop="articleBody">
<?php echo $this->item->text; ?>
</div>
<?php if (isset($urls) && ((!empty($urls->urls_position) && $urls->urls_position == '1') || ($params->get('urls_position') == '1'))) : ?>
<?php echo $this->loadTemplate('links'); ?>
<?php endif; ?>
<?php elseif ($params->get('show_noauth') == true && $this->user->get('guest')) : ?>
<?php echo LayoutHelper::render('joomla.content.intro_image', $this->item); ?>
<?php echo HTMLHelper::_('content.prepare', $this->item->introtext); ?>
<?php endif; ?>
<?php echo $this->item->event->afterDisplayContent; ?>
<?php if (isset($info) && ($info == 1 || $info == 2)) : ?>
<?php if ($params->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
<?php echo LayoutHelper::render('joomla.content.tags', $this->item->tags->itemTags); ?>
<?php endif; ?>
<?php echo LayoutHelper::render('joomla.content.info_block', ['item' => $this->item, 'params' => $params, 'position' => 'below']); ?>
<?php endif; ?>
</div>
<!-- Table of Contents - Right Side -->
<div class="col-lg-3 col-md-4 order-md-2 mb-4">
<div class="sticky-top toc-wrapper" style="top: 20px;">
<nav id="toc" data-toggle="toc" class="toc-container">
<h5 class="toc-title"><?php echo HTMLHelper::_('string.truncate', $this->item->title, 50); ?></h5>
</nav>
</div>
</div>
</div>
</div>
<style>
.toc-container {
background: var(--cassiopeia-color-bg, #fff);
border: 1px solid var(--cassiopeia-color-border, #dee2e6);
border-radius: 0.375rem;
padding: 1rem;
}
<?php if ($info == 1 || $info == 2) : ?>
<?php if ($useDefList) : ?>
<?php echo LayoutHelper::render('joomla.content.info_block', ['item' => $this->item, 'params' => $params, 'position' => 'below']); ?>
<?php endif; ?>
<?php if ($params->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
<?php $this->item->tagLayout = new FileLayout('joomla.content.tags'); ?>
<?php echo $this->item->tagLayout->render($this->item->tags->itemTags); ?>
<?php endif; ?>
<?php endif; ?>
<?php
if (!empty($this->item->pagination) && $this->item->paginationposition && !$this->item->paginationrelative) :
echo $this->item->pagination;
?>
<?php endif; ?>
<?php if ((int) $params->get('urls_position', 0) === 1) : ?>
<?php echo $this->loadTemplate('links'); ?>
<?php endif; ?>
<?php // Optional teaser intro text for guests ?>
<?php elseif ($params->get('show_noauth') == true && $user->get('guest')) : ?>
<?php echo LayoutHelper::render('joomla.content.intro_image', $this->item); ?>
<?php echo HTMLHelper::_('content.prepare', $this->item->introtext); ?>
<?php // Optional link to let them register to see the whole article. ?>
<?php if ($params->get('show_readmore') && $this->item->fulltext != null) : ?>
<?php $menu = Factory::getApplication()->getMenu(); ?>
<?php $active = $menu->getActive(); ?>
<?php $itemId = $active->id; ?>
<?php $link = new Uri(Route::_('index.php?option=com_users&view=login&Itemid=' . $itemId, false)); ?>
<?php $link->setVar('return', base64_encode(RouteHelper::getArticleRoute($this->item->slug, $this->item->catid, $this->item->language))); ?>
<?php echo LayoutHelper::render('joomla.content.readmore', ['item' => $this->item, 'params' => $params, 'link' => $link]); ?>
<?php endif; ?>
<?php endif; ?>
<?php
if (!empty($this->item->pagination) && $this->item->paginationposition && $this->item->paginationrelative) :
echo $this->item->pagination;
?>
<?php endif; ?>
<?php // Content is generated by content plugin event "onContentAfterDisplay" ?>
<?php echo $this->item->event->afterDisplayContent; ?>
</div>
.toc-title {
margin-bottom: 0.75rem;
font-size: 1rem;
font-weight: 600;
color: var(--cassiopeia-color-text, #212529);
border-bottom: 1px solid var(--cassiopeia-color-border, #dee2e6);
padding-bottom: 0.5rem;
}
@media (max-width: 767.98px) {
.toc-wrapper {
position: static !important;
margin-bottom: 1.5rem;
}
}
</style>

View File

@@ -1,33 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage com_content
*
* @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\LayoutHelper;
// Add strings for translations in Javascript.
Text::script('JGLOBAL_EXPAND_CATEGORIES');
Text::script('JGLOBAL_COLLAPSE_CATEGORIES');
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->document->getWebAssetManager();
$wa->getRegistry()->addExtensionRegistryFile('com_categories');
$wa->usePreset('com_categories.shared-categories-accordion');
?>
<div class="com-content-categories categories-list">
<?php
echo LayoutHelper::render('joomla.content.categories_default', $this);
echo $this->loadTemplate('items');
?>
</div>

View File

@@ -1,77 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage com_content
*
* @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;
use Joomla\Component\Content\Site\Helper\RouteHelper;
if ($this->maxLevelcat != 0 && count($this->items[$this->parent->id]) > 0) :
?>
<div class="com-content-categories__items">
<?php foreach ($this->items[$this->parent->id] as $id => $item) : ?>
<?php if ($this->params->get('show_empty_categories_cat') || $item->numitems || count($item->getChildren())) : ?>
<div class="com-content-categories__item">
<div class="com-content-categories__item-title-wrapper">
<div class="com-content-categories__item-title">
<a href="<?php echo Route::_(RouteHelper::getCategoryRoute($item->id, $item->language)); ?>">
<?php echo $this->escape($item->title); ?></a>
<?php if ($this->params->get('show_cat_num_articles_cat') == 1) :?>
<span class="badge bg-info">
<?php echo Text::_('COM_CONTENT_NUM_ITEMS'); ?>&nbsp;
<?php echo $item->numitems; ?>
</span>
<?php endif; ?>
</div>
<?php if (count($item->getChildren()) > 0 && $this->maxLevelcat > 1) : ?>
<button
type="button"
id="category-btn-<?php echo $item->id; ?>"
data-category-id="<?php echo $item->id; ?>"
class="btn btn-secondary btn-sm"
aria-expanded="false"
aria-label="<?php echo Text::_('JGLOBAL_EXPAND_CATEGORIES'); ?>"
>
<span class="icon-plus" aria-hidden="true"></span>
</button>
<?php endif; ?>
</div>
<?php if ($this->params->get('show_description_image') && $item->getParams()->get('image')) : ?>
<?php echo HTMLHelper::_('image', $item->getParams()->get('image'), $item->getParams()->get('image_alt')); ?>
<?php endif; ?>
<?php if ($this->params->get('show_subcat_desc_cat') == 1) : ?>
<?php if ($item->description) : ?>
<div class="com-content-categories__description category-desc">
<?php echo HTMLHelper::_('content.prepare', $item->description, '', 'com_content.categories'); ?>
</div>
<?php endif; ?>
<?php endif; ?>
<?php if (count($item->getChildren()) > 0 && $this->maxLevelcat > 1) : ?>
<div class="com-content-categories__children" id="category-<?php echo $item->id; ?>" hidden="">
<?php
$this->items[$item->id] = $item->getChildren();
$this->parent = $item;
$this->maxLevelcat--;
echo $this->loadTemplate('items');
$this->parent = $item->getParent();
$this->maxLevelcat++;
?>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
<?php endforeach; ?>
</div>
<?php endif; ?>

View File

@@ -1,81 +0,0 @@
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
This file is part of a Moko Consulting project.
SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
DEFGROUP: Joomla.Templates.Site
INGROUP: Moko-Cassiopeia
FILE: index.html
BRIEF: Security redirect page to block folder access and forward to site root.
-->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Redirecting…</title>
<!-- Search engines: do not index this placeholder redirect page -->
<meta name="robots" content="noindex, nofollow, noarchive" />
<!-- Instant redirect fallback even if JavaScript is disabled -->
<meta http-equiv="refresh" content="0; url=/" />
<!-- Canonical root reference -->
<link rel="canonical" href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
(function redirectToRoot() {
// Configuration object with safe defaults.
var opts = {
fallbackPath: "/", // string: fallback destination if origin is unavailable
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
behavior: "replace" // enum: "replace" | "assign"
};
// Determine absolute origin in all mainstream browsers.
var origin = (typeof location.origin === "string" && location.origin)
|| (location.protocol + "//" + location.host);
// Final destination: absolute root of the current site, or fallback path.
var destination = origin ? origin + "/" : opts.fallbackPath;
function go() {
if (opts.behavior === "assign") {
location.assign(destination);
} else {
location.replace(destination);
}
}
// Execute redirect, optionally after a short delay.
if (opts.delayMs > 0) {
setTimeout(go, opts.delayMs);
} else {
go();
}
})();
</script>
<!--
Secondary meta-refresh for no-JS environments is already set above.
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
-->
<noscript>
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
<style>
html, body { height:100%; }
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
.msg { opacity: .75; text-align: center; }
</style>
</noscript>
</head>
<body>
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
</body>
</html>

View File

@@ -1,143 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage com_content
*
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\FileLayout;
use Joomla\CMS\Layout\LayoutHelper;
$app = Factory::getApplication();
$this->category->text = $this->category->description;
$app->triggerEvent('onContentPrepare', [$this->category->extension . '.categories', &$this->category, &$this->params, 0]);
$this->category->description = $this->category->text;
$results = $app->triggerEvent('onContentAfterTitle', [$this->category->extension . '.categories', &$this->category, &$this->params, 0]);
$afterDisplayTitle = trim(implode("\n", $results));
$results = $app->triggerEvent('onContentBeforeDisplay', [$this->category->extension . '.categories', &$this->category, &$this->params, 0]);
$beforeDisplayContent = trim(implode("\n", $results));
$results = $app->triggerEvent('onContentAfterDisplay', [$this->category->extension . '.categories', &$this->category, &$this->params, 0]);
$afterDisplayContent = trim(implode("\n", $results));
$htag = $this->params->get('show_page_heading') ? 'h2' : 'h1';
?>
<div class="com-content-category-blog blog" itemscope itemtype="https://schema.org/Blog">
<?php if ($this->params->get('show_page_heading')) : ?>
<div class="page-header">
<h1> <?php echo $this->escape($this->params->get('page_heading')); ?> </h1>
</div>
<?php endif; ?>
<?php if ($this->params->get('show_category_title', 1)) : ?>
<<?php echo $htag; ?>>
<?php echo $this->category->title; ?>
</<?php echo $htag; ?>>
<?php endif; ?>
<?php echo $afterDisplayTitle; ?>
<?php if ($this->params->get('show_cat_tags', 1) && !empty($this->category->tags->itemTags)) : ?>
<?php $this->category->tagLayout = new FileLayout('joomla.content.tags'); ?>
<?php echo $this->category->tagLayout->render($this->category->tags->itemTags); ?>
<?php endif; ?>
<?php if ($beforeDisplayContent || $afterDisplayContent || $this->params->get('show_description', 1) || $this->params->def('show_description_image', 1)) : ?>
<div class="category-desc clearfix">
<?php if ($this->params->get('show_description_image') && $this->category->getParams()->get('image')) : ?>
<?php echo LayoutHelper::render(
'joomla.html.image',
[
'src' => $this->category->getParams()->get('image'),
'alt' => empty($this->category->getParams()->get('image_alt')) && empty($this->category->getParams()->get('image_alt_empty')) ? false : $this->category->getParams()->get('image_alt'),
]
); ?>
<?php endif; ?>
<?php echo $beforeDisplayContent; ?>
<?php if ($this->params->get('show_description') && $this->category->description) : ?>
<?php echo HTMLHelper::_('content.prepare', $this->category->description, '', 'com_content.category'); ?>
<?php endif; ?>
<?php echo $afterDisplayContent; ?>
</div>
<?php endif; ?>
<?php if (empty($this->lead_items) && empty($this->link_items) && empty($this->intro_items)) : ?>
<?php if ($this->params->get('show_no_articles', 1)) : ?>
<div class="alert alert-info">
<span class="icon-info-circle" aria-hidden="true"></span><span class="visually-hidden"><?php echo Text::_('INFO'); ?></span>
<?php echo Text::_('COM_CONTENT_NO_ARTICLES'); ?>
</div>
<?php endif; ?>
<?php endif; ?>
<?php if (!empty($this->lead_items)) : ?>
<div class="com-content-category-blog__items blog-items items-leading <?php echo $this->params->get('blog_class_leading'); ?>">
<?php foreach ($this->lead_items as &$item) : ?>
<div class="com-content-category-blog__item blog-item" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
<?php
$this->item = &$item;
echo $this->loadTemplate('item');
?>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if (!empty($this->intro_items)) : ?>
<?php $blogClass = $this->params->get('blog_class', ''); ?>
<?php if ((int) $this->params->get('num_columns') > 1) : ?>
<?php $blogClass .= (int) $this->params->get('multi_column_order', 0) === 0 ? ' masonry-' : ' columns-'; ?>
<?php $blogClass .= (int) $this->params->get('num_columns'); ?>
<?php endif; ?>
<div class="com-content-category-blog__items blog-items <?php echo $blogClass; ?>">
<?php foreach ($this->intro_items as $key => &$item) : ?>
<div class="com-content-category-blog__item blog-item"
itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
<?php
$this->item = & $item;
echo $this->loadTemplate('item');
?>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if (!empty($this->link_items)) : ?>
<div class="items-more">
<?php echo $this->loadTemplate('links'); ?>
</div>
<?php endif; ?>
<?php if ($this->maxLevel != 0 && !empty($this->children[$this->category->id])) : ?>
<div class="com-content-category-blog__children cat-children">
<?php if ($this->params->get('show_category_heading_title_text', 1) == 1) : ?>
<h3> <?php echo Text::_('JGLOBAL_SUBCATEGORIES'); ?> </h3>
<?php endif; ?>
<?php echo $this->loadTemplate('children'); ?> </div>
<?php endif; ?>
<?php if (($this->params->def('show_pagination', 1) == 1 || ($this->params->get('show_pagination') == 2)) && ($this->pagination->pagesTotal > 1)) : ?>
<div class="com-content-category-blog__navigation w-100">
<?php if ($this->params->def('show_pagination_results', 1)) : ?>
<p class="com-content-category-blog__counter counter float-end pt-3 pe-2">
<?php echo $this->pagination->getPagesCounter(); ?>
</p>
<?php endif; ?>
<div class="com-content-category-blog__pagination">
<?php echo $this->pagination->getPagesLinks(); ?>
</div>
</div>
<?php endif; ?>
</div>

View File

@@ -1,86 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage com_content
*
* @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;
use Joomla\Component\Content\Site\Helper\RouteHelper;
$lang = Factory::getLanguage();
$user = Factory::getUser();
$groups = $user->getAuthorisedViewLevels();
if ($this->maxLevel != 0 && count($this->children[$this->category->id]) > 0) : ?>
<?php foreach ($this->children[$this->category->id] as $id => $child) : ?>
<?php // Check whether category access level allows access to subcategories. ?>
<?php if (in_array($child->access, $groups)) : ?>
<?php if ($this->params->get('show_empty_categories') || $child->numitems || count($child->getChildren())) : ?>
<div class="com-content-category-blog__child">
<?php if ($lang->isRtl()) : ?>
<h3 class="page-header item-title">
<?php if ($this->params->get('show_cat_num_articles', 1)) : ?>
<span class="badge bg-info tip">
<?php echo $child->getNumItems(true); ?>
</span>
<?php endif; ?>
<a href="<?php echo Route::_(RouteHelper::getCategoryRoute($child->id, $child->language)); ?>">
<?php echo $this->escape($child->title); ?></a>
<?php if ($this->maxLevel > 1 && count($child->getChildren()) > 0) : ?>
<a href="#category-<?php echo $child->id; ?>" data-bs-toggle="collapse" class="btn btn-sm float-end" aria-label="<?php echo Text::_('JGLOBAL_EXPAND_CATEGORIES'); ?>"><span class="icon-plus" aria-hidden="true"></span></a>
<?php endif; ?>
</h3>
<?php else : ?>
<h3 class="page-header item-title"><a href="<?php echo Route::_(RouteHelper::getCategoryRoute($child->id, $child->language)); ?>">
<?php echo $this->escape($child->title); ?></a>
<?php if ($this->params->get('show_cat_num_articles', 1)) : ?>
<span class="badge bg-info">
<?php echo Text::_('COM_CONTENT_NUM_ITEMS'); ?>&nbsp;
<?php echo $child->getNumItems(true); ?>
</span>
<?php endif; ?>
<?php if ($this->maxLevel > 1 && count($child->getChildren()) > 0) : ?>
<a href="#category-<?php echo $child->id; ?>" data-bs-toggle="collapse" class="btn btn-sm float-end" aria-label="<?php echo Text::_('JGLOBAL_EXPAND_CATEGORIES'); ?>"><span class="icon-plus" aria-hidden="true"></span></a>
<?php endif; ?>
</h3>
<?php endif; ?>
<?php if ($this->params->get('show_subcat_desc') == 1) : ?>
<?php if ($child->description) : ?>
<div class="com-content-category-blog__description category-desc">
<?php echo HTMLHelper::_('content.prepare', $child->description, '', 'com_content.category'); ?>
</div>
<?php endif; ?>
<?php endif; ?>
<?php if ($this->maxLevel > 1 && count($child->getChildren()) > 0) : ?>
<div class="com-content-category-blog__children collapse fade" id="category-<?php echo $child->id; ?>">
<?php
$this->children[$child->id] = $child->getChildren();
$this->category = $child;
$this->maxLevel--;
echo $this->loadTemplate('children');
$this->category = $child->getParent();
$this->maxLevel++;
?>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
<?php endif; ?>
<?php endforeach; ?>
<?php endif;

View File

@@ -1,102 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage com_content
*
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Associations;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Uri\Uri;
use Joomla\Component\Content\Administrator\Extension\ContentComponent;
use Joomla\Component\Content\Site\Helper\RouteHelper;
// Create a shortcut for params.
$params = $this->item->params;
$canEdit = $this->item->params->get('access-edit');
$info = $params->get('info_block_position', 0);
// Check if associations are implemented. If they are, define the parameter.
$assocParam = (Associations::isEnabled() && $params->get('show_associations'));
$currentDate = Factory::getDate()->format('Y-m-d H:i:s');
$isUnpublished = ($this->item->state == ContentComponent::CONDITION_UNPUBLISHED || $this->item->publish_up > $currentDate)
|| ($this->item->publish_down < $currentDate && $this->item->publish_down !== null);
?>
<section id="<?php echo $this->item->alias;?>">
<?php echo LayoutHelper::render('joomla.content.intro_image', $this->item); ?>
<div class="item-content">
<?php if ($isUnpublished) : ?>
<div class="system-unpublished">
<?php endif; ?>
<?php echo LayoutHelper::render('joomla.content.blog_style_default_item_title', $this->item); ?>
<?php if ($canEdit) : ?>
<?php echo LayoutHelper::render('joomla.content.icons', ['params' => $params, 'item' => $this->item]); ?>
<?php endif; ?>
<?php // @todo Not that elegant would be nice to group the params ?>
<?php $useDefList = ($params->get('show_modify_date') || $params->get('show_publish_date') || $params->get('show_create_date')
|| $params->get('show_hits') || $params->get('show_category') || $params->get('show_parent_category') || $params->get('show_author') || $assocParam); ?>
<?php if ($useDefList && ($info == 0 || $info == 2)) : ?>
<?php echo LayoutHelper::render('joomla.content.info_block', ['item' => $this->item, 'params' => $params, 'position' => 'above']); ?>
<?php endif; ?>
<?php if ($info == 0 && $params->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
<?php echo LayoutHelper::render('joomla.content.tags', $this->item->tags->itemTags); ?>
<?php endif; ?>
<?php if (!$params->get('show_intro')) : ?>
<?php // Content is generated by content plugin event "onContentAfterTitle" ?>
<?php echo $this->item->event->afterDisplayTitle; ?>
<?php endif; ?>
<?php // Content is generated by content plugin event "onContentBeforeDisplay" ?>
<?php echo $this->item->event->beforeDisplayContent; ?>
<?php echo $this->item->introtext; ?>
<?php if ($info == 1 || $info == 2) : ?>
<?php if ($useDefList) : ?>
<?php echo LayoutHelper::render('joomla.content.info_block', ['item' => $this->item, 'params' => $params, 'position' => 'below']); ?>
<?php endif; ?>
<?php if ($params->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
<?php echo LayoutHelper::render('joomla.content.tags', $this->item->tags->itemTags); ?>
<?php endif; ?>
<?php endif; ?>
<?php if ($params->get('show_readmore') && $this->item->readmore) :
if ($params->get('access-view')) :
$link = Route::_(RouteHelper::getArticleRoute($this->item->slug, $this->item->catid, $this->item->language));
else :
$menu = Factory::getApplication()->getMenu();
$active = $menu->getActive();
$itemId = $active->id;
$link = new Uri(Route::_('index.php?option=com_users&view=login&Itemid=' . $itemId, false));
$link->setVar('return', base64_encode(RouteHelper::getArticleRoute($this->item->slug, $this->item->catid, $this->item->language)));
endif; ?>
<?php echo LayoutHelper::render('joomla.content.readmore', ['item' => $this->item, 'params' => $params, 'link' => $link]); ?>
<?php endif; ?>
<?php if ($isUnpublished) : ?>
</div>
<?php endif; ?>
<?php // Content is generated by content plugin event "onContentAfterDisplay" ?>
<?php echo $this->item->event->afterDisplayContent; ?>
</div>
</section>

View File

@@ -1,27 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage com_content
*
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\Router\Route;
use Joomla\Component\Content\Site\Helper\RouteHelper;
?>
<ol class="com-content-blog__links">
<?php foreach ($this->link_items as $item) : ?>
<li class="com-content-blog__link">
<a href="<?php echo Route::_(RouteHelper::getArticleRoute($item->slug, $item->catid, $item->language)); ?>">
<?php echo $item->title; ?></a>
</li>
<?php endforeach; ?>
</ol>

View File

@@ -1,25 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage com_content
*
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\Layout\LayoutHelper;
?>
<div class="com-content-category category-list">
<?php
$this->subtemplatename = 'articles';
echo LayoutHelper::render('joomla.content.category_default', $this);
?>
</div>

View File

@@ -1,349 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage com_content
*
* @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Associations;
use Joomla\CMS\Language\Multilanguage;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Uri\Uri;
use Joomla\Component\Content\Administrator\Extension\ContentComponent;
use Joomla\Component\Content\Site\Helper\AssociationHelper;
use Joomla\Component\Content\Site\Helper\RouteHelper;
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->document->getWebAssetManager();
$wa->useScript('com_content.articles-list');
// Create some shortcuts.
$n = count($this->items);
$listOrder = $this->escape($this->state->get('list.ordering'));
$listDirn = $this->escape($this->state->get('list.direction'));
$langFilter = false;
// Tags filtering based on language filter
if (($this->params->get('filter_field') === 'tag') && (Multilanguage::isEnabled())) {
$tagfilter = ComponentHelper::getParams('com_tags')->get('tag_list_language_filter');
switch ($tagfilter) {
case 'current_language':
$langFilter = Factory::getApplication()->getLanguage()->getTag();
break;
case 'all':
$langFilter = false;
break;
default:
$langFilter = $tagfilter;
}
}
// Check for at least one editable article
$isEditable = false;
if (!empty($this->items)) {
foreach ($this->items as $article) {
if ($article->params->get('access-edit')) {
$isEditable = true;
break;
}
}
}
$currentDate = Factory::getDate()->format('Y-m-d H:i:s');
?>
<form action="<?php echo htmlspecialchars(Uri::getInstance()->toString()); ?>" method="post" name="adminForm" id="adminForm" class="com-content-category__articles">
<?php if ($this->params->get('filter_field') !== 'hide') : ?>
<div class="com-content__filter btn-group">
<?php if ($this->params->get('filter_field') === 'tag') : ?>
<span class="visually-hidden">
<label class="filter-search-lbl" for="filter-search">
<?php echo Text::_('JOPTION_SELECT_TAG'); ?>
</label>
</span>
<select name="filter_tag" id="filter-search" class="form-select" onchange="document.adminForm.submit();" >
<option value=""><?php echo Text::_('JOPTION_SELECT_TAG'); ?></option>
<?php echo HTMLHelper::_('select.options', HTMLHelper::_('tag.options', ['filter.published' => [1], 'filter.language' => $langFilter], true), 'value', 'text', $this->state->get('filter.tag')); ?>
</select>
<?php elseif ($this->params->get('filter_field') === 'month') : ?>
<span class="visually-hidden">
<label class="filter-search-lbl" for="filter-search">
<?php echo Text::_('JOPTION_SELECT_MONTH'); ?>
</label>
</span>
<select name="filter-search" id="filter-search" class="form-select" onchange="document.adminForm.submit();">
<option value=""><?php echo Text::_('JOPTION_SELECT_MONTH'); ?></option>
<?php echo HTMLHelper::_('select.options', HTMLHelper::_('content.months', $this->state), 'value', 'text', $this->state->get('list.filter')); ?>
</select>
<?php else : ?>
<label class="filter-search-lbl visually-hidden" for="filter-search">
<?php echo Text::_('COM_CONTENT_' . $this->params->get('filter_field') . '_FILTER_LABEL'); ?>
</label>
<input type="text" name="filter-search" id="filter-search" value="<?php echo $this->escape($this->state->get('list.filter')); ?>" class="inputbox" onchange="document.adminForm.submit();" placeholder="<?php echo Text::_('COM_CONTENT_' . $this->params->get('filter_field') . '_FILTER_LABEL'); ?>">
<?php endif; ?>
<?php if ($this->params->get('filter_field') !== 'tag' && $this->params->get('filter_field') !== 'month') : ?>
<button type="submit" name="filter_submit" class="btn btn-primary"><?php echo Text::_('JGLOBAL_FILTER_BUTTON'); ?></button>
<?php endif; ?>
<button type="reset" name="filter-clear-button" class="btn btn-secondary"><?php echo Text::_('JSEARCH_FILTER_CLEAR'); ?></button>
</div>
<?php endif; ?>
<?php if ($this->params->get('show_pagination_limit')) : ?>
<div class="com-content-category__pagination btn-group float-end">
<label for="limit" class="visually-hidden">
<?php echo Text::_('JGLOBAL_DISPLAY_NUM'); ?>
</label>
<?php echo $this->pagination->getLimitBox(); ?>
</div>
<?php endif; ?>
<?php if (empty($this->items)) : ?>
<?php if ($this->params->get('show_no_articles', 1)) : ?>
<div class="alert alert-info">
<span class="icon-info-circle" aria-hidden="true"></span><span class="visually-hidden"><?php echo Text::_('INFO'); ?></span>
<?php echo Text::_('COM_CONTENT_NO_ARTICLES'); ?>
</div>
<?php endif; ?>
<?php else : ?>
<table class="com-content-category__table category table table-striped table-bordered table-hover">
<caption class="visually-hidden">
<?php echo Text::_('COM_CONTENT_ARTICLES_TABLE_CAPTION'); ?>
</caption>
<thead<?php echo $this->params->get('show_headings', '1') ? '' : ' class="visually-hidden"'; ?>>
<tr>
<th scope="col" id="categorylist_header_title">
<?php echo HTMLHelper::_('grid.sort', 'JGLOBAL_TITLE', 'a.title', $listDirn, $listOrder, null, 'asc', '', 'adminForm'); ?>
</th>
<?php if ($date = $this->params->get('list_show_date')) : ?>
<th scope="col" id="categorylist_header_date">
<?php if ($date === 'created') : ?>
<?php echo HTMLHelper::_('grid.sort', 'COM_CONTENT_' . $date . '_DATE', 'a.created', $listDirn, $listOrder); ?>
<?php elseif ($date === 'modified') : ?>
<?php echo HTMLHelper::_('grid.sort', 'COM_CONTENT_' . $date . '_DATE', 'a.modified', $listDirn, $listOrder); ?>
<?php elseif ($date === 'published') : ?>
<?php echo HTMLHelper::_('grid.sort', 'COM_CONTENT_' . $date . '_DATE', 'a.publish_up', $listDirn, $listOrder); ?>
<?php endif; ?>
</th>
<?php endif; ?>
<?php if ($this->params->get('list_show_author')) : ?>
<th scope="col" id="categorylist_header_author">
<?php echo HTMLHelper::_('grid.sort', 'JAUTHOR', 'author', $listDirn, $listOrder); ?>
</th>
<?php endif; ?>
<?php if ($this->params->get('list_show_hits')) : ?>
<th scope="col" id="categorylist_header_hits">
<?php echo HTMLHelper::_('grid.sort', 'JGLOBAL_HITS', 'a.hits', $listDirn, $listOrder); ?>
</th>
<?php endif; ?>
<?php if ($this->params->get('list_show_votes', 0) && $this->vote) : ?>
<th scope="col" id="categorylist_header_votes">
<?php echo HTMLHelper::_('grid.sort', 'COM_CONTENT_VOTES', 'rating_count', $listDirn, $listOrder); ?>
</th>
<?php endif; ?>
<?php if ($this->params->get('list_show_ratings', 0) && $this->vote) : ?>
<th scope="col" id="categorylist_header_ratings">
<?php echo HTMLHelper::_('grid.sort', 'COM_CONTENT_RATINGS', 'rating', $listDirn, $listOrder); ?>
</th>
<?php endif; ?>
<?php if ($isEditable) : ?>
<th scope="col" id="categorylist_header_edit"><?php echo Text::_('COM_CONTENT_EDIT_ITEM'); ?></th>
<?php endif; ?>
</tr>
</thead>
<tbody>
<?php foreach ($this->items as $i => $article) : ?>
<?php if ($this->items[$i]->state == ContentComponent::CONDITION_UNPUBLISHED) : ?>
<tr class="system-unpublished cat-list-row<?php echo $i % 2; ?>">
<?php else : ?>
<tr class="cat-list-row<?php echo $i % 2; ?>" >
<?php endif; ?>
<th class="list-title" scope="row">
<?php if (in_array($article->access, $this->user->getAuthorisedViewLevels())) : ?>
<a href="<?php echo Route::_(RouteHelper::getArticleRoute($article->slug, $article->catid, $article->language)); ?>">
<?php echo $this->escape($article->title); ?>
</a>
<?php if (Associations::isEnabled() && $this->params->get('show_associations')) : ?>
<div class="cat-list-association">
<?php $associations = AssociationHelper::displayAssociations($article->id); ?>
<?php foreach ($associations as $association) : ?>
<?php if ($this->params->get('flags', 1) && $association['language']->image) : ?>
<?php $flag = HTMLHelper::_('image', 'mod_languages/' . $association['language']->image . '.gif', $association['language']->title_native, ['title' => $association['language']->title_native], true); ?>
<a href="<?php echo Route::_($association['item']); ?>"><?php echo $flag; ?></a>
<?php else : ?>
<?php $class = 'btn btn-secondary btn-sm btn-' . strtolower($association['language']->lang_code); ?>
<a class="<?php echo $class; ?>" title="<?php echo $association['language']->title_native; ?>" href="<?php echo Route::_($association['item']); ?>"><?php echo $association['language']->lang_code; ?>
<span class="visually-hidden"><?php echo $association['language']->title_native; ?></span>
</a>
<?php endif; ?>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php else : ?>
<?php
echo $this->escape($article->title) . ' : ';
$itemId = Factory::getApplication()->getMenu()->getActive()->id;
$link = new Uri(Route::_('index.php?option=com_users&view=login&Itemid=' . $itemId, false));
$link->setVar('return', base64_encode(RouteHelper::getArticleRoute($article->slug, $article->catid, $article->language)));
?>
<a href="<?php echo $link; ?>" class="register">
<?php echo Text::_('COM_CONTENT_REGISTER_TO_READ_MORE'); ?>
</a>
<?php if (Associations::isEnabled() && $this->params->get('show_associations')) : ?>
<div class="cat-list-association">
<?php $associations = AssociationHelper::displayAssociations($article->id); ?>
<?php foreach ($associations as $association) : ?>
<?php if ($this->params->get('flags', 1)) : ?>
<?php $flag = HTMLHelper::_('image', 'mod_languages/' . $association['language']->image . '.gif', $association['language']->title_native, ['title' => $association['language']->title_native], true); ?>
<a href="<?php echo Route::_($association['item']); ?>"><?php echo $flag; ?></a>
<?php else : ?>
<?php $class = 'btn btn-secondary btn-sm btn-' . strtolower($association['language']->lang_code); ?>
<a class="<?php echo $class; ?>" title="<?php echo $association['language']->title_native; ?>" href="<?php echo Route::_($association['item']); ?>"><?php echo $association['language']->lang_code; ?>
<span class="visually-hidden"><?php echo $association['language']->title_native; ?></span>
</a>
<?php endif; ?>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php endif; ?>
<?php if ($article->state == ContentComponent::CONDITION_UNPUBLISHED) : ?>
<div>
<span class="list-published badge bg-warning text-light">
<?php echo Text::_('JUNPUBLISHED'); ?>
</span>
</div>
<?php endif; ?>
<?php if ($article->publish_up > $currentDate) : ?>
<div>
<span class="list-published badge bg-warning text-light">
<?php echo Text::_('JNOTPUBLISHEDYET'); ?>
</span>
</div>
<?php endif; ?>
<?php if (!is_null($article->publish_down) && $article->publish_down < $currentDate) : ?>
<div>
<span class="list-published badge bg-warning text-light">
<?php echo Text::_('JEXPIRED'); ?>
</span>
</div>
<?php endif; ?>
</th>
<?php if ($this->params->get('list_show_date')) : ?>
<td class="list-date small">
<?php
echo HTMLHelper::_(
'date',
$article->displayDate,
$this->escape($this->params->get('date_format', Text::_('DATE_FORMAT_LC3')))
); ?>
</td>
<?php endif; ?>
<?php if ($this->params->get('list_show_author', 1)) : ?>
<td class="list-author">
<?php if (!empty($article->author) || !empty($article->created_by_alias)) : ?>
<?php $author = $article->author ?>
<?php $author = $article->created_by_alias ?: $author; ?>
<?php if (!empty($article->contact_link) && $this->params->get('link_author') == true) : ?>
<?php if ($this->params->get('show_headings')) : ?>
<?php echo HTMLHelper::_('link', $article->contact_link, $author); ?>
<?php else : ?>
<?php echo Text::sprintf('COM_CONTENT_WRITTEN_BY', HTMLHelper::_('link', $article->contact_link, $author)); ?>
<?php endif; ?>
<?php else : ?>
<?php if ($this->params->get('show_headings')) : ?>
<?php echo $author; ?>
<?php else : ?>
<?php echo Text::sprintf('COM_CONTENT_WRITTEN_BY', $author); ?>
<?php endif; ?>
<?php endif; ?>
<?php endif; ?>
</td>
<?php endif; ?>
<?php if ($this->params->get('list_show_hits', 1)) : ?>
<td class="list-hits">
<span class="badge bg-info">
<?php if ($this->params->get('show_headings')) : ?>
<?php echo $article->hits; ?>
<?php else : ?>
<?php echo Text::sprintf('JGLOBAL_HITS_COUNT', $article->hits); ?>
<?php endif; ?>
</span>
</td>
<?php endif; ?>
<?php if ($this->params->get('list_show_votes', 0) && $this->vote) : ?>
<td class="list-votes">
<span class="badge bg-success">
<?php if ($this->params->get('show_headings')) : ?>
<?php echo $article->rating_count; ?>
<?php else : ?>
<?php echo Text::sprintf('COM_CONTENT_VOTES_COUNT', $article->rating_count); ?>
<?php endif; ?>
</span>
</td>
<?php endif; ?>
<?php if ($this->params->get('list_show_ratings', 0) && $this->vote) : ?>
<td class="list-ratings">
<span class="badge bg-warning text-light">
<?php if ($this->params->get('show_headings')) : ?>
<?php echo $article->rating; ?>
<?php else : ?>
<?php echo Text::sprintf('COM_CONTENT_RATINGS_COUNT', $article->rating); ?>
<?php endif; ?>
</span>
</td>
<?php endif; ?>
<?php if ($isEditable) : ?>
<td class="list-edit">
<?php if ($article->params->get('access-edit')) : ?>
<?php echo HTMLHelper::_('contenticon.edit', $article, $article->params); ?>
<?php endif; ?>
</td>
<?php endif; ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<?php // Code to add a link to submit an article. ?>
<?php if ($this->category->getParams()->get('access-create')) : ?>
<?php echo HTMLHelper::_('contenticon.create', $this->category, $this->category->params); ?>
<?php endif; ?>
<?php // Add pagination links ?>
<?php if (!empty($this->items)) : ?>
<?php if (($this->params->def('show_pagination', 2) == 1 || ($this->params->get('show_pagination') == 2)) && ($this->pagination->pagesTotal > 1)) : ?>
<div class="com-content-category__navigation w-100">
<?php if ($this->params->def('show_pagination_results', 1)) : ?>
<p class="com-content-category__counter counter float-end pt-3 pe-2">
<?php echo $this->pagination->getPagesCounter(); ?>
</p>
<?php endif; ?>
<div class="com-content-category__pagination">
<?php echo $this->pagination->getPagesLinks(); ?>
</div>
</div>
<?php endif; ?>
<?php endif; ?>
<div>
<input type="hidden" name="filter_order" value="">
<input type="hidden" name="filter_order_Dir" value="">
<input type="hidden" name="limitstart" value="">
<input type="hidden" name="task" value="">
</div>
</form>

View File

@@ -1,85 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage com_content
*
* @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;
use Joomla\Component\Content\Site\Helper\RouteHelper;
$lang = Factory::getLanguage();
$user = Factory::getUser();
$groups = $user->getAuthorisedViewLevels();
?>
<?php if (count($this->children[$this->category->id]) > 0) : ?>
<?php foreach ($this->children[$this->category->id] as $id => $child) : ?>
<?php // Check whether category access level allows access to subcategories. ?>
<?php if (in_array($child->access, $groups)) : ?>
<?php if ($this->params->get('show_empty_categories') || $child->getNumItems(true) || count($child->getChildren())) : ?>
<div class="com-content-category__children">
<?php if ($lang->isRtl()) : ?>
<h3 class="page-header item-title">
<?php if ($this->params->get('show_cat_num_articles', 1)) : ?>
<span class="badge bg-info tip hasTooltip" title="<?php echo HTMLHelper::_('tooltipText', 'COM_CONTENT_NUM_ITEMS'); ?>">
<?php echo $child->getNumItems(true); ?>
</span>
<?php endif; ?>
<a href="<?php echo Route::_(RouteHelper::getCategoryRoute($child->id, $child->language)); ?>">
<?php echo $this->escape($child->title); ?></a>
<?php if (count($child->getChildren()) > 0 && $this->maxLevel > 1) : ?>
<a href="#category-<?php echo $child->id; ?>" data-bs-toggle="collapse" class="btn btn-sm float-end" aria-label="<?php echo Text::_('JGLOBAL_EXPAND_CATEGORIES'); ?>"><span class="icon-plus" aria-hidden="true"></span></a>
<?php endif; ?>
</h3>
<?php else : ?>
<h3 class="page-header item-title"><a href="<?php echo Route::_(RouteHelper::getCategoryRoute($child->id, $child->language)); ?>">
<?php echo $this->escape($child->title); ?></a>
<?php if ($this->params->get('show_cat_num_articles', 1)) : ?>
<span class="badge bg-info tip hasTooltip" title="<?php echo HTMLHelper::_('tooltipText', 'COM_CONTENT_NUM_ITEMS'); ?>">
<?php echo $child->getNumItems(true); ?>
</span>
<?php endif; ?>
<?php if (count($child->getChildren()) > 0 && $this->maxLevel > 1) : ?>
<a href="#category-<?php echo $child->id; ?>" data-bs-toggle="collapse" class="btn btn-sm float-end" aria-label="<?php echo Text::_('JGLOBAL_EXPAND_CATEGORIES'); ?>"><span class="icon-plus" aria-hidden="true"></span></a>
<?php endif; ?>
</h3>
<?php endif; ?>
<?php if ($this->params->get('show_subcat_desc') == 1) : ?>
<?php if ($child->description) : ?>
<div class="category-desc">
<?php echo HTMLHelper::_('content.prepare', $child->description, '', 'com_content.category'); ?>
</div>
<?php endif; ?>
<?php endif; ?>
<?php if (count($child->getChildren()) > 0 && $this->maxLevel > 1) : ?>
<div class="collapse fade" id="category-<?php echo $child->id; ?>">
<?php
$this->children[$child->id] = $child->getChildren();
$this->category = $child;
$this->maxLevel--;
echo $this->loadTemplate('children');
$this->category = $child->getParent();
$this->maxLevel++;
?>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
<?php endif; ?>
<?php endforeach; ?>
<?php endif; ?>

View File

@@ -1,81 +0,0 @@
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
This file is part of a Moko Consulting project.
SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
DEFGROUP: Joomla.Templates.Site
INGROUP: Moko-Cassiopeia
FILE: index.html
BRIEF: Security redirect page to block folder access and forward to site root.
-->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Redirecting…</title>
<!-- Search engines: do not index this placeholder redirect page -->
<meta name="robots" content="noindex, nofollow, noarchive" />
<!-- Instant redirect fallback even if JavaScript is disabled -->
<meta http-equiv="refresh" content="0; url=/" />
<!-- Canonical root reference -->
<link rel="canonical" href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
(function redirectToRoot() {
// Configuration object with safe defaults.
var opts = {
fallbackPath: "/", // string: fallback destination if origin is unavailable
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
behavior: "replace" // enum: "replace" | "assign"
};
// Determine absolute origin in all mainstream browsers.
var origin = (typeof location.origin === "string" && location.origin)
|| (location.protocol + "//" + location.host);
// Final destination: absolute root of the current site, or fallback path.
var destination = origin ? origin + "/" : opts.fallbackPath;
function go() {
if (opts.behavior === "assign") {
location.assign(destination);
} else {
location.replace(destination);
}
}
// Execute redirect, optionally after a short delay.
if (opts.delayMs > 0) {
setTimeout(go, opts.delayMs);
} else {
go();
}
})();
</script>
<!--
Secondary meta-refresh for no-JS environments is already set above.
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
-->
<noscript>
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
<style>
html, body { height:100%; }
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
.msg { opacity: .75; text-align: center; }
</style>
</noscript>
</head>
<body>
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
</body>
</html>

View File

@@ -1,75 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage com_content
*
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
?>
<div class="blog-featured" itemscope itemtype="https://schema.org/Blog">
<?php if ($this->params->get('show_page_heading') != 0) : ?>
<div class="page-header">
<h1>
<?php echo $this->escape($this->params->get('page_heading')); ?>
</h1>
</div>
<?php endif; ?>
<?php if (!empty($this->lead_items)) : ?>
<div class="blog-items items-leading <?php echo $this->params->get('blog_class_leading'); ?>">
<?php foreach ($this->lead_items as &$item) : ?>
<div class="blog-item"
itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
<?php
$this->item = & $item;
echo $this->loadTemplate('item');
?>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if (!empty($this->intro_items)) : ?>
<?php $blogClass = $this->params->get('blog_class', ''); ?>
<?php if ((int) $this->params->get('num_columns') > 1) : ?>
<?php $blogClass .= (int) $this->params->get('multi_column_order', 0) === 0 ? ' masonry-' : ' columns-'; ?>
<?php $blogClass .= (int) $this->params->get('num_columns'); ?>
<?php endif; ?>
<div class="blog-items <?php echo $blogClass; ?>">
<?php foreach ($this->intro_items as $key => &$item) : ?>
<div class="blog-item"
itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
<?php
$this->item = & $item;
echo $this->loadTemplate('item');
?>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if (!empty($this->link_items)) : ?>
<div class="items-more">
<?php echo $this->loadTemplate('links'); ?>
</div>
<?php endif; ?>
<?php if ($this->params->def('show_pagination', 2) == 1 || ($this->params->get('show_pagination') == 2 && $this->pagination->pagesTotal > 1)) : ?>
<div class="w-100">
<?php if ($this->params->def('show_pagination_results', 1)) : ?>
<p class="counter float-end pt-3 pe-2">
<?php echo $this->pagination->getPagesCounter(); ?>
</p>
<?php endif; ?>
<?php echo $this->pagination->getPagesLinks(); ?>
</div>
<?php endif; ?>
</div>

View File

@@ -1,121 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage com_content
*
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Associations;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Uri\Uri;
use Joomla\Component\Content\Administrator\Extension\ContentComponent;
use Joomla\Component\Content\Site\Helper\RouteHelper;
// Create a shortcut for params.
$params = &$this->item->params;
$canEdit = $this->item->params->get('access-edit');
$info = $this->item->params->get('info_block_position', 0);
// Check if associations are implemented. If they are, define the parameter.
$assocParam = (Associations::isEnabled() && $params->get('show_associations'));
$currentDate = Factory::getDate()->format('Y-m-d H:i:s');
$isExpired = !is_null($this->item->publish_down) && $this->item->publish_down < $currentDate;
$isNotPublishedYet = $this->item->publish_up > $currentDate;
$isUnpublished = $this->item->state == ContentComponent::CONDITION_UNPUBLISHED || $isNotPublishedYet || $isExpired;
?>
<?php echo LayoutHelper::render('joomla.content.intro_image', $this->item); ?>
<div class="item-content">
<?php if ($isUnpublished) : ?>
<div class="system-unpublished">
<?php endif; ?>
<?php if ($params->get('show_title')) : ?>
<h2 class="item-title" itemprop="headline">
<?php if ($params->get('link_titles') && $params->get('access-view')) : ?>
<a href="<?php echo Route::_(RouteHelper::getArticleRoute($this->item->slug, $this->item->catid, $this->item->language)); ?>" itemprop="url">
<?php echo $this->escape($this->item->title); ?>
</a>
<?php else : ?>
<?php echo $this->escape($this->item->title); ?>
<?php endif; ?>
</h2>
<?php endif; ?>
<?php if ($this->item->state == ContentComponent::CONDITION_UNPUBLISHED) : ?>
<span class="badge bg-warning text-light"><?php echo Text::_('JUNPUBLISHED'); ?></span>
<?php endif; ?>
<?php if ($isNotPublishedYet) : ?>
<span class="badge bg-warning text-light"><?php echo Text::_('JNOTPUBLISHEDYET'); ?></span>
<?php endif; ?>
<?php if ($isExpired) : ?>
<span class="badge bg-warning text-light"><?php echo Text::_('JEXPIRED'); ?></span>
<?php endif; ?>
<?php if ($canEdit) : ?>
<?php echo LayoutHelper::render('joomla.content.icons', ['params' => $params, 'item' => $this->item]); ?>
<?php endif; ?>
<?php // Content is generated by content plugin event "onContentAfterTitle" ?>
<?php echo $this->item->event->afterDisplayTitle; ?>
<?php // @todo Not that elegant would be nice to group the params ?>
<?php $useDefList = ($params->get('show_modify_date') || $params->get('show_publish_date') || $params->get('show_create_date')
|| $params->get('show_hits') || $params->get('show_category') || $params->get('show_parent_category') || $params->get('show_author') || $assocParam); ?>
<?php if ($useDefList && ($info == 0 || $info == 2)) : ?>
<?php echo LayoutHelper::render('joomla.content.info_block', ['item' => $this->item, 'params' => $params, 'position' => 'above']); ?>
<?php endif; ?>
<?php if ($info == 0 && $params->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
<?php echo LayoutHelper::render('joomla.content.tags', $this->item->tags->itemTags); ?>
<?php endif; ?>
<?php // Content is generated by content plugin event "onContentBeforeDisplay" ?>
<?php echo $this->item->event->beforeDisplayContent; ?>
<?php echo $this->item->introtext; ?>
<?php if ($info == 1 || $info == 2) : ?>
<?php if ($useDefList) : ?>
<?php echo LayoutHelper::render('joomla.content.info_block', ['item' => $this->item, 'params' => $params, 'position' => 'below']); ?>
<?php endif; ?>
<?php if ($params->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
<?php echo LayoutHelper::render('joomla.content.tags', $this->item->tags->itemTags); ?>
<?php endif; ?>
<?php endif; ?>
<?php if ($params->get('show_readmore') && $this->item->readmore) :
if ($params->get('access-view')) :
$link = Route::_(RouteHelper::getArticleRoute($this->item->slug, $this->item->catid, $this->item->language));
else :
$menu = Factory::getApplication()->getMenu();
$active = $menu->getActive();
$itemId = $active->id;
$link = new Uri(Route::_('index.php?option=com_users&view=login&Itemid=' . $itemId, false));
$link->setVar('return', base64_encode(RouteHelper::getArticleRoute($this->item->slug, $this->item->catid, $this->item->language)));
endif; ?>
<?php echo LayoutHelper::render('joomla.content.readmore', ['item' => $this->item, 'params' => $params, 'link' => $link]); ?>
<?php endif; ?>
<?php if ($isUnpublished) : ?>
</div>
<?php endif; ?>
</div>
<?php // Content is generated by content plugin event "onContentAfterDisplay" ?>
<?php echo $this->item->event->afterDisplayContent; ?>

View File

@@ -1,26 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage com_content
*
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\Router\Route;
use Joomla\Component\Content\Site\Helper\RouteHelper;
?>
<ol class="com-content-blog__links">
<?php foreach ($this->link_items as $item) : ?>
<li class="com-content-blog__link">
<a href="<?php echo Route::_(RouteHelper::getArticleRoute($item->slug, $item->catid, $item->language)); ?>">
<?php echo $item->title; ?></a>
</li>
<?php endforeach; ?>
</ol>

View File

@@ -1,81 +0,0 @@
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
This file is part of a Moko Consulting project.
SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
DEFGROUP: Joomla.Templates.Site
INGROUP: Moko-Cassiopeia
FILE: index.html
BRIEF: Security redirect page to block folder access and forward to site root.
-->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Redirecting…</title>
<!-- Search engines: do not index this placeholder redirect page -->
<meta name="robots" content="noindex, nofollow, noarchive" />
<!-- Instant redirect fallback even if JavaScript is disabled -->
<meta http-equiv="refresh" content="0; url=/" />
<!-- Canonical root reference -->
<link rel="canonical" href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
(function redirectToRoot() {
// Configuration object with safe defaults.
var opts = {
fallbackPath: "/", // string: fallback destination if origin is unavailable
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
behavior: "replace" // enum: "replace" | "assign"
};
// Determine absolute origin in all mainstream browsers.
var origin = (typeof location.origin === "string" && location.origin)
|| (location.protocol + "//" + location.host);
// Final destination: absolute root of the current site, or fallback path.
var destination = origin ? origin + "/" : opts.fallbackPath;
function go() {
if (opts.behavior === "assign") {
location.assign(destination);
} else {
location.replace(destination);
}
}
// Execute redirect, optionally after a short delay.
if (opts.delayMs > 0) {
setTimeout(go, opts.delayMs);
} else {
go();
}
})();
</script>
<!--
Secondary meta-refresh for no-JS environments is already set above.
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
-->
<noscript>
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
<style>
html, body { height:100%; }
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
.msg { opacity: .75; text-align: center; }
</style>
</noscript>
</head>
<body>
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
</body>
</html>

View File

@@ -1,81 +0,0 @@
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
This file is part of a Moko Consulting project.
SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
DEFGROUP: Joomla.Templates.Site
INGROUP: Moko-Cassiopeia
FILE: index.html
BRIEF: Security redirect page to block folder access and forward to site root.
-->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Redirecting…</title>
<!-- Search engines: do not index this placeholder redirect page -->
<meta name="robots" content="noindex, nofollow, noarchive" />
<!-- Instant redirect fallback even if JavaScript is disabled -->
<meta http-equiv="refresh" content="0; url=/" />
<!-- Canonical root reference -->
<link rel="canonical" href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
(function redirectToRoot() {
// Configuration object with safe defaults.
var opts = {
fallbackPath: "/", // string: fallback destination if origin is unavailable
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
behavior: "replace" // enum: "replace" | "assign"
};
// Determine absolute origin in all mainstream browsers.
var origin = (typeof location.origin === "string" && location.origin)
|| (location.protocol + "//" + location.host);
// Final destination: absolute root of the current site, or fallback path.
var destination = origin ? origin + "/" : opts.fallbackPath;
function go() {
if (opts.behavior === "assign") {
location.assign(destination);
} else {
location.replace(destination);
}
}
// Execute redirect, optionally after a short delay.
if (opts.delayMs > 0) {
setTimeout(go, opts.delayMs);
} else {
go();
}
})();
</script>
<!--
Secondary meta-refresh for no-JS environments is already set above.
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
-->
<noscript>
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
<style>
html, body { height:100%; }
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
.msg { opacity: .75; text-align: center; }
</style>
</noscript>
</head>
<body>
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
</body>
</html>

View File

@@ -1,78 +0,0 @@
<?php
/**
* @package AkeebaEngage
* @copyright Copyright (c)2020-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
defined('_JEXEC') or die();
/**
* View Template for comments display
*
* This is the main view template used when comments are being displayed e.g. at the end of an article.
*
* This provides the outer HTML structure of the comments.
*
* It loads the following view templates:
* - default_list.php The threaded list of comments
* - default_login.php Login form for guest users
* - default_form.php Comment / reply submission form
*/
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Language\Text;
/** @var \Akeeba\Component\Engage\Site\View\Comments\HtmlView $this */
$cParams = ComponentHelper::getParams('com_engage');
?>
<section id="akengage-comments-section" class="akengage-outer-container"
aria-label="<?= Text::_('COM_ENGAGE_COMMENTS_SECTION_HEADER') ?>">
<h3 class="akengage-title h4 border-bottom mb-2" data-toc-skip>
<?= Text::plural($this->headerKey, $this->pagination->total, $this->title) ?>
</h3>
<?= $this->loadPosition('engage-before-comments') ?>
<?php if ($this->pagination->total): ?>
<div class="akengage-list-container">
<?= $this->loadTemplate('list') ?>
</div>
<?= $this->loadPosition('engage-after-comments') ?>
<?php if ($this->pagination->pagesTotal > 1): ?>
<div class="akengage-pagination">
<div class="akengage-pagination-pages pagination" itemscope itemtype="http://www.schema.org/SiteNavigationElement">
<?= $this->pagination->getPagesLinks() ?>
</div>
</div>
<?php endif; ?>
<?php endif; ?>
<?php if (!$this->areCommentsClosed && $this->user->guest && !$this->perms['create']): ?>
<?= $this->loadTemplate('login') ?>
<?php endif; ?>
<?php if ($this->perms['create'] && !$this->areCommentsClosed): ?>
<?= $this->loadTemplate('form') ?>
<?php endif; ?>
<?php if ($this->perms['create'] && $this->areCommentsClosed): ?>
<div class="alert alert-info">
<h3 class="alert-heading">
<?= Text::_('COM_ENGAGE_COMMENTS_LBL_CLOSED_HEADER') ?>
</h3>
<p>
<?php if ($this->areCommentsClosedAfterTime): ?>
<?= Text::_('COM_ENGAGE_COMMENTS_LBL_CLOSED_AFTERTIME') ?>
<?php else: ?>
<?= Text::_('COM_ENGAGE_COMMENTS_LBL_CLOSED_BODY') ?>
<?php endif; ?>
</p>
</div>
<?php endif; ?>
</section>

View File

@@ -1,90 +0,0 @@
<?php
/**
* @package AkeebaEngage
* @copyright Copyright (c)2020-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
defined('_JEXEC') or die();
/**
* View Template for the submitting comments.
*
* This is called by default.php
*/
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Uri\Uri;
/** @var \Akeeba\Component\Engage\Site\View\Comments\HtmlView $this */
$cParams = ComponentHelper::getParams('com_engage');
$badUx = ($cParams->get('comments_reply_bad_ux', 0) == 1) && empty($this->form->getValue('body'));
HTMLHelper::_('behavior.formvalidator');
?>
<?php if ($badUx): ?>
<div class="akengage-comment-hider" id="akengage-comment-hider">
<button type="button"
id="akengage-comment-hider-button"
class="btn btn-primary">
<?= Text::_('COM_ENGAGE_COMMENTS_FORM_HEADER'); ?>
</button>
</div>
<?php endif; ?>
<form action="<?= Route::_('index.php?option=com_engage&task=comment.save') ?>"
method="post" name="akengage-comment-form" id="akengageCommentForm"
class="form-validate <?= $badUx ? 'd-none' : ''; ?>"
style="<?= $badUx ? 'display: none;' : ''; ?>"
aria-label="<?= Text::_('COM_ENGAGE_COMMENTS_FORM_HEADER', true) ?>"
>
<input type="hidden" name="returnurl" value="<?= base64_encode(Uri::getInstance()->toString(['scheme', 'user', 'pass', 'host', 'port', 'path', 'query', 'fragment'])) ?>">
<input type="hidden" name="view" value="">
<input type="hidden" name="id" value="">
<?= HTMLHelper::_('form.token') ?>
<div class="mt-3 pt-2 mb-2 border-top border-2 border-dark">
<h3 class="h1 my-3">
<?= Text::_('COM_ENGAGE_COMMENTS_FORM_HEADER') ?>
</h3>
<?= $this->loadPosition('engage-before-reply'); ?>
<div id="akengage-comment-inreplyto-wrapper" class="alert alert-info d-none">
<div class="d-flex flex-wrap">
<div class="flex-grow-1">
<?= Text::_('COM_ENGAGE_COMMENTS_FORM_INREPLYTO_LABEL'); ?>
<span id="akengage-comment-inreplyto-name" class="text-secondary fw-bold">Some User</span>
</div>
<button id="akengage-comment-inreplyto-cancel"
type="button"
class="ms-2 btn btn-sm btn-outline-danger"
><?= Text::_('COM_ENGAGE_COMMENTS_FORM_CANCELREPLY'); ?></button>
</div>
</div>
<?php foreach (array_keys($this->form->getFieldsets()) as $fieldSet)
{
echo $this->form->renderFieldset($fieldSet);
} ?>
<div class="control-group">
<div class="controls">
<button type="submit"
class="btn btn-lg btn-primary w-100 akengage-comment-submit-btn"
>
<span class="fa fa-comment-dots" aria-hidden="true"></span>
<?= Text::_('COM_ENGAGE_COMMENTS_FORM_EDIT_BTN_SUBMIT') ?>
</button>
</div>
</div>
<?= $this->loadPosition('engage-after-reply'); ?>
</div>
</form>

View File

@@ -1,286 +0,0 @@
<?php
/**
* @package AkeebaEngage
* @copyright Copyright (c)2020-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
defined('_JEXEC') or die();
/**
* View Template for the threaded display of comments.
*
* Loaded from default.php
*/
use Akeeba\Component\Engage\Administrator\Helper\Avatar;
use Akeeba\Component\Engage\Administrator\Helper\UserFetcher;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Uri\Uri;
use Joomla\CMS\User\User;
// Maximum avatar width, in pixels.
$maxAvatarWidth = 48;
/** @var \Akeeba\Component\Engage\Site\View\Comments\HtmlView $this */
$previousLevel = 0;
$openListItem = 0;
$parentIds = [0 => 0];
$parentNames = [0 => ''];
foreach ($this->items as $comment):
$user = !empty($comment->created_by) && empty($comment->name) ? UserFetcher::getUser($comment->created_by) : new User();
if (empty($comment->created_by) || !empty($comment->name)) {
$user->name = $comment->name;
$user->email = $comment->email;
}
$parentIds[$comment->depth] = $comment->id;
$parentNames[$comment->depth] = $user->name;
// Deeper level comment. Indent with <ul> tags
if ($comment->depth > $previousLevel):
?>
<?php for ($level = $previousLevel + 1; $level <= $comment->depth; $level++): ?>
<ul class="akengage-comment-list akengage-comment-list--level<?= $level ?> list-unstyled">
<?php endfor; ?>
<?php // Shallower level comment. Outdent with </ul> tags
elseif ($comment->depth < $previousLevel): ?>
<?php if ($openListItem): $openListItem--; ?>
</li>
<?php endif; ?>
<?php for ($level = $previousLevel - 1; $level >= $comment->depth; $level--): ?>
</ul>
<?php if ($openListItem): $openListItem--; ?>
</li>
<?php endif; ?>
<?php endfor; ?>
<?php // Same level comment. Close the <li> tag.
else: ?>
<?php $openListItem--; ?>
</li>
<?php endif; ?>
<?php
$previousLevel = $comment->depth;
$avatar = Avatar::getUserAvatar($comment->created_by, $maxAvatarWidth, $comment->email);
$profile = Avatar::getProfileURL($user);
$commentDate = Factory::getDate($comment->created)->setTimezone($this->userTimezone);
$ipLookupURL = $this->getIPLookupURL($comment->ip);
$isModified = !empty($comment->modified_by) && !empty($comment->modified) && (
empty($comment->created_by) || empty($comment->created) || (
($comment->modified_by != $comment->created_by) &&
($comment->modified != $comment->created)
)
);
if ($isModified)
{
if ($comment->modified_by == $comment->created_by)
{
// If the comment is modified by the created by user, use the public name determined at the top of the file.
$modifiedBy = $user->name;
}
else
{
// Someone else modified the comment. Use their name.
$modifiedUser = UserFetcher::getUser($comment->modified_by);
// If the user is no longer available, use '???'
$modifiedBy = ($modifiedUser === null || $modifiedUser->guest) ? Text::_('COM_ENGAGE_LBL_COMMENT_MODIFIED_NO_LONGER_AVAILABLE') : $modifiedUser->name;
}
}
$openListItem++;
$this->ensureHasParentInfo($comment, $parentIds, $parentNames);
$bsCommentStateClass = ($comment->enabled == 1) ? 'secondary' : (($comment->enabled == -3) ? 'warning' : 'danger')
?>
<li class="akengage-comment-item mb-2">
<article
class="akengage-comment--<?= ($comment->enabled == 1) ? 'primary' : (($comment->enabled == -3) ? 'spam' : 'unpublished') ?> border-start border-4 border-<?= $bsCommentStateClass ?> ps-2 mb-2"
id="akengage-comment-<?= $comment->id ?>" itemscope itemtype="http://schema.org/Comment">
<span itemprop="dateCreated" content="<?= $commentDate->toISO8601(false) ?>"></span>
<span itemprop="datePublished" content="<?= $commentDate->toISO8601(false) ?>"></span>
<footer
itemprop="author" itemscope itemtype="http://schema.org/Person"
class="akengage-comment-properties d-flex flex-row gap-1 mb-1 bg-light p-1 small border-bottom border-2">
<?php if (!empty($avatar)): ?>
<div class="akengage-commenter-avatar-container d-none d-sm-block flex-shrink-1" style="max-width: <?= (int) $maxAvatarWidth ?>px">
<?php if (empty($profile)): ?>
<img src="<?= $avatar ?>" alt="" class="akengage-commenter-avatar img-fluid rounded-3 shadow-sm" itemprop="image">
<?php else: ?>
<a href="<?= $profile ?>" class="akengage-commenter-profile" itemprop="url" rel="noopener">
<img src="<?= $avatar ?>"
alt=""
class="akengage-commenter-avatar img-fluid rounded-3 shadow-sm" itemprop="image">
</a>
<?php endif; ?>
</div>
<?php endif; ?>
<div class="akengange-comment-head d-flex flex-column w-100">
<div class="akengange-commenter-name d-flex flex-row flex-wrap gap-3 align-items-center mb-1">
<span itemprop="name" class="fw-bold flex-grow-1"><?= $this->escape($user->name) ?></span>
<?php if ($this->perms['state']): ?>
<div>
<?php if ($user->authorise('core.manage', $comment->asset_id)): ?>
<span class="akengage-commenter-ismoderator fa fa-star text-warning" aria-hidden="true"></span>
<?php elseif (!$user->guest): ?>
<span class="akengage-commenter-isuser fa fa-user text-secondary" aria-hidden="true"></span>
<?php endif; ?>
<?php if (!$user->guest): ?>
<span class="akengage-commenter-username font-monospace text-success"><?= $this->escape($user->username) ?></span>
<?php elseif ($this->perms['state']): ?>
<span class="akengage-commenter-isguest fa fa-user-friends text-danger" aria-hidden="true"></span>
<span class="akengage-commenter-email font-monospace text-muted"><?= $this->escape($user->email) ?></span>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
<div class="akengage-comment-info d-flex flex-row flex-wrap gap-2 align-items-center">
<div class="akengage-comment-permalink flex-grow-1">
<?php
$tempUri = clone Uri::getInstance();
$tempUri->setFragment(sprintf('akengage-comment-%u', $comment->id));
$tempUri->setVar('akengage_cid', $comment->id);
?>
<a href="<?= $tempUri->toString() ?>"
class="text-body text-decoration-none"
>
<?= $commentDate->format(Text::_('DATE_FORMAT_LC2'), true) ?>
</a>
</div>
<div class="akengage-comment-actions d-flex gap-1">
<?php if ($this->perms['state']): ?>
<span class="akengage-comment-publish_unpublish">
<?php if ($comment->enabled == 1): ?>
<button class="akengage-comment-unpublish-btn btn btn-sm btn-outline-secondary"
data-akengageid="<?= $comment->id ?>">
<?= Text::_('COM_ENGAGE_COMMENTS_BTN_UNPUBLISH') ?>
</button>
<?php elseif ($comment->enabled == 0): ?>
<button class="akengage-comment-publish-btn btn btn-sm btn-outline-secondary"
data-akengageid="<?= $comment->id ?>">
<?= Text::_('COM_ENGAGE_COMMENTS_BTN_PUBLISH') ?>
</button>
<?php endif; ?>
</span>
<?php if($comment->enabled == -3): ?>
<span class="akengage-comment-mark-ham">
<button class="akengage-comment-markham-btn btn btn-sm btn-outline-success"
data-akengageid="<?= $comment->id ?>"
title="<?= Text::_('COM_ENGAGE_COMMENTS_BTN_MARKHAM_TITLE') ?>">
<?= Text::_('COM_ENGAGE_COMMENTS_BTN_MARKHAM') ?>
</button>
</span>
<?php if ($this->perms['delete']): ?>
<span class="akengage-comment-mark-spam">
<button class="akengage-comment-markspam-btn btn btn-sm btn-outline-danger"
data-akengageid="<?= $comment->id ?>"
title="<?= Text::_('COM_ENGAGE_COMMENTS_BTN_MARKSPAM_TITLE') ?>">
<?= Text::_('COM_ENGAGE_COMMENTS_BTN_MARKSPAM') ?>
</button>
</span>
<?php endif; ?>
<?php else: ?>
<span class="akengage-comment-mark-possiblespam">
<button class="akengage-comment-possiblespam-btn btn btn-sm btn-outline-warning"
data-akengageid="<?= $comment->id ?>"
title="<?= Text::_('COM_ENGAGE_COMMENTS_BTN_POSSIBLESPAM_TITLE') ?>">
<?= Text::_('COM_ENGAGE_COMMENTS_BTN_POSSIBLESPAM') ?>
</button>
</span>
<?php endif; ?>
<?php endif; ?>
<?php if ($this->perms['delete']): ?>
<span class="akengage-comment-delete">
<button class="akengage-comment-delete-btn btn btn-sm btn-outline-danger"
data-akengageid="<?= $comment->id ?>">
<?= Text::_('COM_ENGAGE_COMMENTS_BTN_DELETE') ?>
</button>
</span>
<?php endif; ?>
<?php if ($this->perms['edit'] || (($this->user->id === $user->id) && $this->perms['own'])): ?>
<span class="akengage-comment-edit">
<button class="akengage-comment-edit-btn btn btn-sm btn-outline-primary"
data-akengageid="<?= $comment->id ?>">
<?= Text::_('COM_ENGAGE_COMMENTS_BTN_EDIT') ?>
</button>
</span>
<?php endif; ?>
</div>
</div>
<?php if ($this->perms['edit'] || $this->user->authorise('core.manage', $comment->asset_id)): ?>
<div>
<?php if (!empty($ipLookupURL)): ?>
<span class="akengage-comment-ip">
<a href="<?= $ipLookupURL ?>" target="_blank">
<?= Text::sprintf('COM_ENGAGE_COMMENTS_IP', $comment->ip ?? '???') ?>
</a>
</span>
<?php else: ?>
<span class="akengage-comment-ip">
<?= Text::sprintf('COM_ENGAGE_COMMENTS_IP', $comment->ip ?? '???') ?>
</span>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
</footer>
<?php if ($comment->enabled == -3): ?>
<div class="akengage-comment-publish-type bg-warning text-white fw-bold p-2">
<?= Text::_('COM_ENGAGE_COMMENTS_TYPE_SPAM') ?>
</div>
<?php elseif ($comment->enabled != 1): ?>
<div class="akengage-comment-publish-type bg-danger text-white fw-bold p-2">
<?= Text::_('COM_ENGAGE_COMMENTS_TYPE_UNPUBLISHED') ?>
</div>
<?php endif ?>
<div class="akengage-comment-body" itemprop="text">
<?= HTMLHelper::_('engage.processCommentTextForDisplay', $comment->body) ?>
<?php if ($isModified): ?>
<div class="my-2 border-top border-1 border-muted text-muted small">
<?= Text::sprintf('COM_ENGAGE_LBL_COMMENT_MODIFIED', Factory::getDate($comment->modified)->setTimezone($this->userTimezone)->format(Text::_('DATE_FORMAT_LC2'), true), $modifiedBy) ?>
</div>
<?php endif; ?>
</div>
<?php if ($this->perms['create']): ?>
<div class="akengage-comment-reply">
<?php // You can reply to $this->maxLevel - 1 level comments only. Replies to deeper nested comments are to the $this->maxLevel - 1 level parent. ?>
<button class="akengage-comment-reply-btn btn btn-sm btn-outline-primary mb-1"
data-akengageid="<?= ($comment->depth < $this->maxLevel) ? $comment->id : $parentIds[$this->maxLevel - 1] ?>"
data-akengagereplyto="<?= $this->escape(($comment->depth < $this->maxLevel) ? $user->name : $parentNames[$this->maxLevel - 1]) ?>"
>
<?= Text::_('COM_ENGAGE_COMMENTS_BTN_REPLY') ?>
</button>
</div>
<?php endif; ?>
</article>
<?php endforeach; ?>
<?php if ($openListItem): ?>
<?php $openListItem--; ?>
</li>
<?php endif; ?>
<?php for ($level = $previousLevel; $level >= 1; $level--): ?>
</ul>
<?php if ($openListItem): ?>
<?php $openListItem--; ?>
</li>
<?php endif; ?>
<?php endfor; ?>

View File

@@ -1,45 +0,0 @@
<?php
/**
* @package AkeebaEngage
* @copyright Copyright (c)2020-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
defined('_JEXEC') or die();
/**
* View Template for the guest users login form
*
* Loaded from default.php
*/
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Language\Text;
/** @var \Akeeba\Component\Engage\Site\View\Comments\HtmlView $this */
$cParams = ComponentHelper::getParams('com_engage');
$loginModule = $cParams->get('login_module', '-1');
$moduleContent = (empty($loginModule) || ($loginModule === '-1')) ? '' : trim($this->loadModule($loginModule));
$positionContent = trim($this->loadPosition('engage-login'));
/**
* A reason for this to happen is that site owner wants discussion to be open to invitation-only members of the site but
* visible by anyone. This is mostly relevant in political organizations, NGOs and local / closed community
* organizations where a small number of people are openly discussing a public interest issue, but they don't want to
* allow random people to detract the conversation.
*/
if (empty($moduleContent) && empty($positionContent))
{
return;
}
?>
<footer id="akeeba-engage-login">
<h4>
<?= Text::_('COM_ENGAGE_COMMENTS_LOGIN_HEAD') ?>
</h4>
<?= $moduleContent ?>
<?= $positionContent ?>
</footer>

View File

@@ -1,81 +0,0 @@
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
This file is part of a Moko Consulting project.
SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
DEFGROUP: Joomla.Templates.Site
INGROUP: Moko-Cassiopeia
FILE: index.html
BRIEF: Security redirect page to block folder access and forward to site root.
-->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Redirecting…</title>
<!-- Search engines: do not index this placeholder redirect page -->
<meta name="robots" content="noindex, nofollow, noarchive" />
<!-- Instant redirect fallback even if JavaScript is disabled -->
<meta http-equiv="refresh" content="0; url=/" />
<!-- Canonical root reference -->
<link rel="canonical" href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
(function redirectToRoot() {
// Configuration object with safe defaults.
var opts = {
fallbackPath: "/", // string: fallback destination if origin is unavailable
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
behavior: "replace" // enum: "replace" | "assign"
};
// Determine absolute origin in all mainstream browsers.
var origin = (typeof location.origin === "string" && location.origin)
|| (location.protocol + "//" + location.host);
// Final destination: absolute root of the current site, or fallback path.
var destination = origin ? origin + "/" : opts.fallbackPath;
function go() {
if (opts.behavior === "assign") {
location.assign(destination);
} else {
location.replace(destination);
}
}
// Execute redirect, optionally after a short delay.
if (opts.delayMs > 0) {
setTimeout(go, opts.delayMs);
} else {
go();
}
})();
</script>
<!--
Secondary meta-refresh for no-JS environments is already set above.
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
-->
<noscript>
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
<style>
html, body { height:100%; }
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
.msg { opacity: .75; text-align: center; }
</style>
</noscript>
</head>
<body>
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
</body>
</html>

View File

@@ -1,81 +0,0 @@
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
This file is part of a Moko Consulting project.
SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
DEFGROUP: Joomla.Templates.Site
INGROUP: Moko-Cassiopeia
FILE: index.html
BRIEF: Security redirect page to block folder access and forward to site root.
-->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Redirecting…</title>
<!-- Search engines: do not index this placeholder redirect page -->
<meta name="robots" content="noindex, nofollow, noarchive" />
<!-- Instant redirect fallback even if JavaScript is disabled -->
<meta http-equiv="refresh" content="0; url=/" />
<!-- Canonical root reference -->
<link rel="canonical" href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
(function redirectToRoot() {
// Configuration object with safe defaults.
var opts = {
fallbackPath: "/", // string: fallback destination if origin is unavailable
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
behavior: "replace" // enum: "replace" | "assign"
};
// Determine absolute origin in all mainstream browsers.
var origin = (typeof location.origin === "string" && location.origin)
|| (location.protocol + "//" + location.host);
// Final destination: absolute root of the current site, or fallback path.
var destination = origin ? origin + "/" : opts.fallbackPath;
function go() {
if (opts.behavior === "assign") {
location.assign(destination);
} else {
location.replace(destination);
}
}
// Execute redirect, optionally after a short delay.
if (opts.delayMs > 0) {
setTimeout(go, opts.delayMs);
} else {
go();
}
})();
</script>
<!--
Secondary meta-refresh for no-JS environments is already set above.
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
-->
<noscript>
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
<style>
html, body { height:100%; }
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
.msg { opacity: .75; text-align: center; }
</style>
</noscript>
</head>
<body>
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
</body>
</html>

View File

@@ -1,81 +0,0 @@
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
This file is part of a Moko Consulting project.
SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
DEFGROUP: Joomla.Templates.Site
INGROUP: Moko-Cassiopeia
FILE: index.html
BRIEF: Security redirect page to block folder access and forward to site root.
-->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Redirecting…</title>
<!-- Search engines: do not index this placeholder redirect page -->
<meta name="robots" content="noindex, nofollow, noarchive" />
<!-- Instant redirect fallback even if JavaScript is disabled -->
<meta http-equiv="refresh" content="0; url=/" />
<!-- Canonical root reference -->
<link rel="canonical" href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
(function redirectToRoot() {
// Configuration object with safe defaults.
var opts = {
fallbackPath: "/", // string: fallback destination if origin is unavailable
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
behavior: "replace" // enum: "replace" | "assign"
};
// Determine absolute origin in all mainstream browsers.
var origin = (typeof location.origin === "string" && location.origin)
|| (location.protocol + "//" + location.host);
// Final destination: absolute root of the current site, or fallback path.
var destination = origin ? origin + "/" : opts.fallbackPath;
function go() {
if (opts.behavior === "assign") {
location.assign(destination);
} else {
location.replace(destination);
}
}
// Execute redirect, optionally after a short delay.
if (opts.delayMs > 0) {
setTimeout(go, opts.delayMs);
} else {
go();
}
})();
</script>
<!--
Secondary meta-refresh for no-JS environments is already set above.
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
-->
<noscript>
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
<style>
html, body { height:100%; }
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
.msg { opacity: .75; text-align: center; }
</style>
</noscript>
</head>
<body>
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
</body>
</html>

View File

@@ -1,61 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage Templates.Moko-Cassiopeia
*
* @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org>
* @copyright (C) 2025 Jonathan Miler || Moko Consulting <https://mokoconsulting.tech>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\Utilities\ArrayHelper;
$module = $displayData['module'];
$params = $displayData['params'];
$attribs = $displayData['attribs'];
if ($module->content === null || $module->content === '') {
return;
}
$moduleTag = $params->get('module_tag', 'div');
$moduleAttribs = [];
$moduleAttribs['class'] = $module->position . ' card ' . htmlspecialchars($params->get('moduleclass_sfx', ''), ENT_QUOTES, 'UTF-8');
$headerTag = htmlspecialchars($params->get('header_tag', 'h3'), ENT_QUOTES, 'UTF-8');
$headerClass = htmlspecialchars($params->get('header_class', ''), ENT_QUOTES, 'UTF-8');
$headerAttribs = [];
$headerAttribs['class'] = $headerClass;
// Only output a header class if it is not card-title
if ($headerClass !== 'card-title') :
$headerAttribs['class'] = 'card-header ' . $headerClass;
endif;
// Only add aria if the moduleTag is not a div
if ($moduleTag !== 'div') {
if ($module->showtitle) :
$moduleAttribs['aria-labelledby'] = 'mod-' . $module->id;
$headerAttribs['id'] = 'mod-' . $module->id;
else :
$moduleAttribs['aria-label'] = $module->title;
endif;
}
$header = '<' . $headerTag . ' ' . ArrayHelper::toString($headerAttribs) . '>' . $module->title . '</' . $headerTag . '>';
?>
<<?php echo $moduleTag; ?> <?php echo ArrayHelper::toString($moduleAttribs); ?>>
<?php if ($module->showtitle && $headerClass !== 'card-title') : ?>
<?php echo $header; ?>
<?php endif; ?>
<div class="card-body">
<?php if ($module->showtitle && $headerClass === 'card-title') : ?>
<?php echo $header; ?>
<?php endif; ?>
<?php echo $module->content; ?>
</div>
</<?php echo $moduleTag; ?>>

View File

@@ -1,88 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage Layout
*
* @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*
* html5 (chosen html5 tag and font header tags)
*/
defined('_JEXEC') or die;
use Joomla\Utilities\ArrayHelper;
$module = $displayData['module'];
$params = $displayData['params'];
//var_dump($module->position);
if ((string) $module->content === '') {
return;
}
$moduleIcon = '';
if ($module->position == 'sidebar-left' || $module->position == 'sidebar-right') {
switch ($module->module) {
case 'mod_virtuemart_cart':
$moduleIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" class="bi bi-cart3" viewBox="0 0 16 16">
<path d="M0 1.5A.5.5 0 0 1 .5 1H2a.5.5 0 0 1 .485.379L2.89 3H14.5a.5.5 0 0 1 .49.598l-1 5a.5.5 0 0 1-.465.401l-9.397.472L4.415 11H13a.5.5 0 0 1 0 1H4a.5.5 0 0 1-.491-.408L2.01 3.607 1.61 2H.5a.5.5 0 0 1-.5-.5M3.102 4l.84 4.479 9.144-.459L13.89 4zM5 12a2 2 0 1 0 0 4 2 2 0 0 0 0-4m7 0a2 2 0 1 0 0 4 2 2 0 0 0 0-4m-7 1a1 1 0 1 1 0 2 1 1 0 0 1 0-2m7 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2"/>
</svg>';
break;
case 'mod_virtuemart_category':
$moduleIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" class="bi bi-list" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5m0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5m0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5"/>
</svg>';
break;
case 'mod_virtuemart_product':
$moduleIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-list-task" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M2 2.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5V3a.5.5 0 0 0-.5-.5zM3 3H2v1h1z"/>
<path d="M5 3.5a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5M5.5 7a.5.5 0 0 0 0 1h9a.5.5 0 0 0 0-1zm0 4a.5.5 0 0 0 0 1h9a.5.5 0 0 0 0-1z"/>
<path fill-rule="evenodd" d="M1.5 7a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5H2a.5.5 0 0 1-.5-.5zM2 7h1v1H2zm0 3.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5zm1 .5H2v1h1z"/>
</svg>';
break;
default:
$moduleIcon = '';
}
}
$moduleTag = htmlspecialchars($params->get('module_tag', 'div'), ENT_QUOTES, 'UTF-8');
$moduleAttribs = [];
$moduleAttribs['class'] = 'moduletable ' . htmlspecialchars($params->get('moduleclass_sfx', ''), ENT_QUOTES, 'UTF-8');
$bootstrapSize = (int) $params->get('bootstrap_size', 0);
$asideCol = ($module->position == 'sidebar-left' || $module->position == 'sidebar-right') ? ' col-md-6' : '';
$footerCol = $module->position == 'footer' ? ' col-md-6' : '';
$moduleAttribs['class'] .= $bootstrapSize !== 0 ? $footerCol . ' col-lg-' . $bootstrapSize : $asideCol;
$headerTag = htmlspecialchars($params->get('header_tag', 'h3'), ENT_QUOTES, 'UTF-8');
$headerClass = htmlspecialchars($params->get('header_class', ''), ENT_QUOTES, 'UTF-8');
$headerAttribs = [];
$headerAttribs['class'] = 'module-title ';
// Only output a header class if one is set
if ($headerClass !== '') {
$headerAttribs['class'] = $headerClass;
}
// Only add aria if the moduleTag is not a div
if ($moduleTag !== 'div') {
if ($module->showtitle) :
$moduleAttribs['aria-labelledby'] = 'mod-' . $module->id;
$headerAttribs['id'] = 'mod-' . $module->id;
else :
$moduleAttribs['aria-label'] = $module->title;
endif;
}
$header = '<' . $headerTag . ' ' . ArrayHelper::toString($headerAttribs) . '>' . $module->title . $moduleIcon . '</' . $headerTag . '>';
?>
<<?php echo $moduleTag; ?> <?php echo ArrayHelper::toString($moduleAttribs); ?>>
<?php if ((bool) $module->showtitle) : ?>
<?php echo $header; ?>
<?php endif; ?>
<?php echo $module->content; ?>
</<?php echo $moduleTag; ?>>

View File

@@ -1,81 +0,0 @@
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
This file is part of a Moko Consulting project.
SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
DEFGROUP: Joomla.Templates.Site
INGROUP: Moko-Cassiopeia
FILE: index.html
BRIEF: Security redirect page to block folder access and forward to site root.
-->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Redirecting…</title>
<!-- Search engines: do not index this placeholder redirect page -->
<meta name="robots" content="noindex, nofollow, noarchive" />
<!-- Instant redirect fallback even if JavaScript is disabled -->
<meta http-equiv="refresh" content="0; url=/" />
<!-- Canonical root reference -->
<link rel="canonical" href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
(function redirectToRoot() {
// Configuration object with safe defaults.
var opts = {
fallbackPath: "/", // string: fallback destination if origin is unavailable
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
behavior: "replace" // enum: "replace" | "assign"
};
// Determine absolute origin in all mainstream browsers.
var origin = (typeof location.origin === "string" && location.origin)
|| (location.protocol + "//" + location.host);
// Final destination: absolute root of the current site, or fallback path.
var destination = origin ? origin + "/" : opts.fallbackPath;
function go() {
if (opts.behavior === "assign") {
location.assign(destination);
} else {
location.replace(destination);
}
}
// Execute redirect, optionally after a short delay.
if (opts.delayMs > 0) {
setTimeout(go, opts.delayMs);
} else {
go();
}
})();
</script>
<!--
Secondary meta-refresh for no-JS environments is already set above.
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
-->
<noscript>
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
<style>
html, body { height:100%; }
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
.msg { opacity: .75; text-align: center; }
</style>
</noscript>
</head>
<body>
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
</body>
</html>

View File

@@ -1,55 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage Templates.Moko-Cassiopeia
*
* @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org>
* @copyright (C) 2025 Jonathan Miler || Moko Consulting <https://mokoconsulting.tech>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\Utilities\ArrayHelper;
$module = $displayData['module'];
$params = $displayData['params'];
$attribs = $displayData['attribs'];
if ($module->content === null || $module->content === '') {
return;
}
$moduleTag = $params->get('module_tag', 'div');
$moduleAttribs = [];
$moduleAttribs['class'] = $module->position . ' no-card ' . htmlspecialchars($params->get('moduleclass_sfx', ''), ENT_QUOTES, 'UTF-8');
$headerTag = htmlspecialchars($params->get('header_tag', 'h3'), ENT_QUOTES, 'UTF-8');
$headerClass = htmlspecialchars($params->get('header_class', ''), ENT_QUOTES, 'UTF-8');
$headerAttribs = [];
// Only output a header class if one is set
if ($headerClass !== '') {
$headerAttribs['class'] = $headerClass;
}
// Only add aria if the moduleTag is not a div
if ($moduleTag !== 'div') {
if ($module->showtitle) :
$moduleAttribs['aria-labelledby'] = 'mod-' . $module->id;
$headerAttribs['id'] = 'mod-' . $module->id;
else :
$moduleAttribs['aria-label'] = $module->title;
endif;
}
$header = '<' . $headerTag . ' ' . ArrayHelper::toString($headerAttribs) . '>' . $module->title . '</' . $headerTag . '>';
?>
<<?php echo $moduleTag; ?> <?php echo ArrayHelper::toString($moduleAttribs); ?>>
<?php if ($module->showtitle) : ?>
<?php echo $header; ?>
<?php endif; ?>
<?php echo $module->content; ?>
</<?php echo $moduleTag; ?>>

View File

@@ -1,81 +0,0 @@
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
This file is part of a Moko Consulting project.
SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
DEFGROUP: Joomla.Templates.Site
INGROUP: Moko-Cassiopeia
FILE: index.html
BRIEF: Security redirect page to block folder access and forward to site root.
-->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Redirecting…</title>
<!-- Search engines: do not index this placeholder redirect page -->
<meta name="robots" content="noindex, nofollow, noarchive" />
<!-- Instant redirect fallback even if JavaScript is disabled -->
<meta http-equiv="refresh" content="0; url=/" />
<!-- Canonical root reference -->
<link rel="canonical" href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
(function redirectToRoot() {
// Configuration object with safe defaults.
var opts = {
fallbackPath: "/", // string: fallback destination if origin is unavailable
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
behavior: "replace" // enum: "replace" | "assign"
};
// Determine absolute origin in all mainstream browsers.
var origin = (typeof location.origin === "string" && location.origin)
|| (location.protocol + "//" + location.host);
// Final destination: absolute root of the current site, or fallback path.
var destination = origin ? origin + "/" : opts.fallbackPath;
function go() {
if (opts.behavior === "assign") {
location.assign(destination);
} else {
location.replace(destination);
}
}
// Execute redirect, optionally after a short delay.
if (opts.delayMs > 0) {
setTimeout(go, opts.delayMs);
} else {
go();
}
})();
</script>
<!--
Secondary meta-refresh for no-JS environments is already set above.
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
-->
<noscript>
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
<style>
html, body { height:100%; }
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
.msg { opacity: .75; text-align: center; }
</style>
</noscript>
</head>
<body>
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
</body>
</html>

View File

@@ -1,34 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage mod_custom
*
* @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org>
* @copyright (C) 2025 Jonathan Miler || Moko Consulting <https://mokoconsulting.tech>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Uri\Uri;
$modId = 'mod-custom' . $module->id;
if ($params->get('backgroundimage')) {
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $app->getDocument()->getWebAssetManager();
$wa->addInlineStyle('
#' . $modId . '{background-image: url("' . Uri::root(true) . '/' . HTMLHelper::_('cleanImageURL', $params->get('backgroundimage'))->url . '");}
', ['name' => $modId]);
}
?>
<div class="mod-custom custom banner-overlay" id="<?php echo $modId; ?>">
<div class="overlay">
<?php echo $module->content; ?>
</div>
</div>

View File

@@ -1,34 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage mod_custom
*
* @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org>
* @copyright (C) 2025 Jonathan Miler || Moko Consulting <https://mokoconsulting.tech>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Uri\Uri;
$modId = 'mod-custom' . $module->id;
if ($params->get('backgroundimage')) {
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $app->getDocument()->getWebAssetManager();
$wa->addInlineStyle('
#' . $modId . '{background-image: url("' . Uri::root(true) . '/' . HTMLHelper::_('cleanImageURL', $params->get('backgroundimage'))->url . '");}
', ['name' => $modId]);
}
?>
<div class="mod-custom custom hero-overlay" id="<?php echo $modId; ?>">
<div class="overlay">
<?php echo $module->content; ?>
</div>
</div>

View File

@@ -1,81 +0,0 @@
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
This file is part of a Moko Consulting project.
SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
DEFGROUP: Joomla.Templates.Site
INGROUP: Moko-Cassiopeia
FILE: index.html
BRIEF: Security redirect page to block folder access and forward to site root.
-->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Redirecting…</title>
<!-- Search engines: do not index this placeholder redirect page -->
<meta name="robots" content="noindex, nofollow, noarchive" />
<!-- Instant redirect fallback even if JavaScript is disabled -->
<meta http-equiv="refresh" content="0; url=/" />
<!-- Canonical root reference -->
<link rel="canonical" href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
(function redirectToRoot() {
// Configuration object with safe defaults.
var opts = {
fallbackPath: "/", // string: fallback destination if origin is unavailable
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
behavior: "replace" // enum: "replace" | "assign"
};
// Determine absolute origin in all mainstream browsers.
var origin = (typeof location.origin === "string" && location.origin)
|| (location.protocol + "//" + location.host);
// Final destination: absolute root of the current site, or fallback path.
var destination = origin ? origin + "/" : opts.fallbackPath;
function go() {
if (opts.behavior === "assign") {
location.assign(destination);
} else {
location.replace(destination);
}
}
// Execute redirect, optionally after a short delay.
if (opts.delayMs > 0) {
setTimeout(go, opts.delayMs);
} else {
go();
}
})();
</script>
<!--
Secondary meta-refresh for no-JS environments is already set above.
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
-->
<noscript>
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
<style>
html, body { height:100%; }
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
.msg { opacity: .75; text-align: center; }
</style>
</noscript>
</head>
<body>
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
</body>
</html>

View File

@@ -1,160 +0,0 @@
<?php
/**
* @package Tabaoca.Component.Gabble.Site
* @subpackage mod_gabble
* @copyright (C) 2023 Jonatas C. Ferreira
* @license GNU/AGPL v3 (see licence.txt)
*/
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Uri\Uri;
$currentuser = Factory::getuser();
$app = Factory::getApplication();
$session = $app->getSession();
$config = $app->getParams('com_gabble');
$document = Factory::getDocument();
$document->addStyleSheet('media/com_gabble/css/gabble.css');
$document->addStyleSheet('media/templates/site/cassiopeia_meaewellness/css/gable.css');
$lang = Factory::getLanguage();
$lang->load('com_gabble');
Text::script('COM_GABBLE_TIMEOUT');
if ( !$currentuser->get("id")){
echo '<div class="content">
<div id="mod_gable">
<div id="mod_lists_gabble">
<div class="taba-user-on"><div class="taba-user"><i class="icon-joomla"></i> '. Text::_('COM_GABBLE_LOGGEDIN') . '</div></div>
</div>
</div>
</div>';
return;
}
$input = $app->input;
if ($input->get('option') == 'com_gabble') {
echo '<div class="content">
<div id="mod_gabble">
<div id="mod_lists_gabble">
<button id="btn_gabble" class="button_list" onclick="window.location.href = &quot;' . Uri::root() . '&quot;;"><i class="icon-home"></i></button>
<div class="taba-user-on"><div class="taba-user"><i class="icon-joomla"></i> '. Text::_('COM_GABBLE_GABBLE_CHAT') . '</div></div>
</div>
</div>
</div>';
return;
}
$document->addScript('media/com_gabble/js/gabble_com.js');
//$document->addScript('media/templates/site/cassiopeia_meaewellness/js/mod_gabblegabble_com.js');
?>
<div class="content">
<div id="mod_gabble">
<div id="list_windows" class="list-windows"></div>
<div id="main_windows" class="main-windows"></div>
<div id="lists_gabble">
<div id="select_list" class="row">
<div class="col-md-4 notifications" title="<?php echo Text::_('COM_GABBLE_CHATS'); ?>">
<button id="list_chats" class="button_list" title="Chats" onclick="select_list(1);"><i class="icon-comments-2"></i></button>
<div id="n_notifications" class="n-notifications" title="Users" hidden=""></div>
</div>
<div class="col-md-4" title="<?php echo Text::_('COM_GABBLE_USERS'); ?>">
<button id="list_users" class="button_list" onclick="select_list(2);"><i class="icon-users"></i></button>
</div>
<div class="col-md-4" title="<?php echo Text::_('COM_GABBLE_GABBLE_CHAT'); ?>">
<button id="btn_gabble" class="button_list" onclick="window.location.href = &quot;<?php echo Uri::root().'index.php?option=com_gabble&view=gabble'; ?>&quot;;"><i class="icon-expand-2"></i></button>
</div>
</div>
<div id="options_list" hidden="">
<select id="users_list" name="users_list" onchange="select_list(2);">
<option value="0"><?php echo Text::_('COM_GABBLE_USERS_ON'); ?></option>
<option value="1"><?php echo Text::_('COM_GABBLE_USERS_ALL'); ?></option>
</select>
</div>
<div id="frame_list">
<iframe
id="users_frame"
class="iframe_list"
srcdoc="<html>
<head>
<link rel=&quot;stylesheet&quot; href=&quot;<?php echo Uri::root().'media/com_gabble/css/gabble.css'; ?>&quot; type=&quot;text/css&quot;/>
</head>
<body class=&quot;taba-content&quot;>
</body>
</html>"
marginwidth="0"
marginheight="0"
onload="setup_com();"
hidden="">
</iframe>
<iframe
id="users_on_frame"
class="iframe_list"
srcdoc="<html>
<head>
<link rel=&quot;stylesheet&quot; href=&quot;<?php echo Uri::root().'media/com_gabble/css/gabble.css'; ?>&quot; type=&quot;text/css&quot;/>
</head>
<body class=&quot;taba-content&quot;>
</body>
</html>"
marginwidth="0"
marginheight="0"
hidden="">
</iframe>
<iframe
id="feeds_frame"
class="iframe_list"
srcdoc="<html>
<head>
<link rel=&quot;stylesheet&quot; href=&quot;<?php echo Uri::root().'media/com_gabble/css/gabble.css'; ?>&quot; type=&quot;text/css&quot;/>
</head>
<body class=&quot;taba-content&quot;>
</body>
</html>"
marginwidth="0"
marginheight="0"
hidden="">
</iframe>
</div>
<div id="openai_btn" title="OpenAi GPT" onclick="open_user(0); event.stopPropagation();">
<img src="<?php echo Uri::root() . "media/com_gabble/images/logo_openai.png"; ?>" alt="OpenAI GPT">
</div>
</div>
</div>
<input type="hidden" id="gabble_type" value="mod">
<input type="hidden" id="uri_root" value="<?php echo Uri::root(); ?>">
<input type="hidden" id="token" value="<?php echo $session->getFormToken(); ?>">
<input type="hidden" id="user_id" value="<?php echo $currentuser->get("id"); ?>">
<input type="hidden" id="openai_gpt" value="<?php echo $config->get('openai_gpt'); ?>">
<input type="hidden" id="openai_gpt_name" value="<?php echo $config->get('openai_gpt_name'); ?>">
</div>
<p style="text-align:right;" ><?php echo Text::_('COM_GABBLE_POWERED');?> <a href="https://tabaoca.org">Tabaoca</a></p>

View File

@@ -1,81 +0,0 @@
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
This file is part of a Moko Consulting project.
SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
DEFGROUP: Joomla.Templates.Site
INGROUP: Moko-Cassiopeia
FILE: index.html
BRIEF: Security redirect page to block folder access and forward to site root.
-->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Redirecting…</title>
<!-- Search engines: do not index this placeholder redirect page -->
<meta name="robots" content="noindex, nofollow, noarchive" />
<!-- Instant redirect fallback even if JavaScript is disabled -->
<meta http-equiv="refresh" content="0; url=/" />
<!-- Canonical root reference -->
<link rel="canonical" href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
(function redirectToRoot() {
// Configuration object with safe defaults.
var opts = {
fallbackPath: "/", // string: fallback destination if origin is unavailable
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
behavior: "replace" // enum: "replace" | "assign"
};
// Determine absolute origin in all mainstream browsers.
var origin = (typeof location.origin === "string" && location.origin)
|| (location.protocol + "//" + location.host);
// Final destination: absolute root of the current site, or fallback path.
var destination = origin ? origin + "/" : opts.fallbackPath;
function go() {
if (opts.behavior === "assign") {
location.assign(destination);
} else {
location.replace(destination);
}
}
// Execute redirect, optionally after a short delay.
if (opts.delayMs > 0) {
setTimeout(go, opts.delayMs);
} else {
go();
}
})();
</script>
<!--
Secondary meta-refresh for no-JS environments is already set above.
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
-->
<noscript>
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
<style>
html, body { height:100%; }
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
.msg { opacity: .75; text-align: center; }
</style>
</noscript>
</head>
<body>
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
</body>
</html>

View File

@@ -1,28 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage mod_menu
*
* @copyright (C) 2021 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
HTMLHelper::_('bootstrap.collapse');
?>
<nav class="navbar navbar-expand-md" aria-label="<?php echo htmlspecialchars($module->title, ENT_QUOTES, 'UTF-8'); ?>">
<button class="navbar-toggler navbar-toggler-right" type="button" data-bs-toggle="collapse" data-bs-target="#navbar<?php echo $module->id; ?>" aria-controls="navbar<?php echo $module->id; ?>" aria-expanded="false" aria-label="<?php echo Text::_('MOD_MENU_TOGGLE'); ?>">
<span class="icon-menu" aria-hidden="true"></span>
</button>
<div class="collapse navbar-collapse" id="navbar<?php echo $module->id; ?>">
<?php require __DIR__ . '/dropdown-metismenu.php'; ?>
</div>
</nav>

View File

@@ -1,110 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage mod_menu
*
* @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org>
* @copyright (C) 2025 Jonathan Miler || Moko Consulting <https://mokoconsulting.tech>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\Helper\ModuleHelper;
use Joomla\Utilities\ArrayHelper;
/** @var \Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $app->getDocument()->getWebAssetManager();
$wa->registerAndUseScript('metismenu', 'media/templates/site/moko-cassiopeia/js/mod_menu/menu-metismenu.min.js', [], ['defer' => true], ['metismenujs']);
$attributes = [];
$attributes['class'] = 'mod-menu mod-menu_dropdown-metismenu metismenu mod-list ' . $class_sfx;
if ($tagId = $params->get('tag_id', '')) {
$attributes['id'] = $tagId;
}
$start = (int) $params->get('startLevel', 1);
?>
<ul <?php echo ArrayHelper::toString($attributes); ?>>
<?php foreach ($list as $i => &$item) {
// Skip sub-menu items if they are set to be hidden in the module's options
if (!$showAll && $item->level > $start) {
continue;
}
$itemParams = $item->getParams();
$class = [];
$class[] = 'metismenu-item item-' . $item->id . ' level-' . ($item->level - $start + 1);
if ($item->id == $default_id) {
$class[] = 'default';
}
if ($item->id == $active_id || ($item->type === 'alias' && $itemParams->get('aliasoptions') == $active_id)) {
$class[] = 'current';
}
if (in_array($item->id, $path)) {
$class[] = 'active';
} elseif ($item->type === 'alias') {
$aliasToId = $itemParams->get('aliasoptions');
if (count($path) > 0 && $aliasToId == $path[count($path) - 1]) {
$class[] = 'active';
} elseif (in_array($aliasToId, $path)) {
$class[] = 'alias-parent-active';
}
}
if ($item->type === 'separator') {
$class[] = 'divider';
}
if ($showAll) {
if ($item->deeper) {
$class[] = 'deeper';
}
if ($item->parent) {
$class[] = 'parent';
}
}
echo '<li class="' . implode(' ', $class) . '">';
switch ($item->type) :
case 'separator':
case 'component':
case 'heading':
case 'url':
require ModuleHelper::getLayoutPath('mod_menu', 'dropdown-metismenu_' . $item->type);
break;
default:
require ModuleHelper::getLayoutPath('mod_menu', 'dropdown-metismenu_url');
endswitch;
switch (true) :
// The next item is deeper.
case $showAll && $item->deeper:
echo '<ul class="mm-collapse">';
break;
// The next item is shallower.
case $item->shallower:
echo '</li>';
echo str_repeat('</ul></li>', $item->level_diff);
break;
// The next item is on the same level.
default:
echo '</li>';
break;
endswitch;
}
?></ul>

View File

@@ -1,79 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage mod_menu
*
* @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org>
* @copyright (C) 2025 Jonathan Miler || Moko Consulting <https://mokoconsulting.tech>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\Filter\OutputFilter;
use Joomla\CMS\HTML\HTMLHelper;
$attributes = [];
if ($item->anchor_title) {
$attributes['title'] = $item->anchor_title;
}
if ($item->anchor_css) {
$attributes['class'] = $item->anchor_css;
}
if ($item->anchor_rel) {
$attributes['rel'] = $item->anchor_rel;
}
if ($item->id == $active_id) {
$attributes['aria-current'] = 'location';
if ($item->current) {
$attributes['aria-current'] = 'page';
}
}
$linktype = $item->title;
if ($item->menu_icon) {
// The link is an icon
if ($itemParams->get('menu_text', 1)) {
// If the link text is to be displayed, the icon is added with aria-hidden
$linktype = '<span class="p-2 ' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
} else {
// If the icon itself is the link, it needs a visually hidden text
$linktype = '<span class="p-2 ' . $item->menu_icon . '" aria-hidden="true"></span><span class="visually-hidden">' . $item->title . '</span>';
}
} elseif ($item->menu_image) {
// The link is an image, maybe with an own class
$image_attributes = [];
if ($item->menu_image_css) {
$image_attributes['class'] = $item->menu_image_css;
}
$linktype = HTMLHelper::_('image', $item->menu_image, $item->title, $image_attributes);
if ($itemParams->get('menu_text', 1)) {
$linktype .= '<span class="image-title">' . $item->title . '</span>';
}
}
if ($item->browserNav == 1) {
$attributes['target'] = '_blank';
} elseif ($item->browserNav == 2) {
$options = 'toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,resizable=yes';
$attributes['onclick'] = "window.open(this.href, 'targetWindow', '" . $options . "'); return false;";
}
echo HTMLHelper::link(OutputFilter::ampReplace(htmlspecialchars($item->flink, ENT_COMPAT, 'UTF-8', false)), $linktype, $attributes);
if ($showAll && $item->deeper) {
echo '<button class="mm-collapsed mm-toggler mm-toggler-link" aria-haspopup="true" aria-expanded="false" aria-label="' . $item->title . '"></button>';
}

View File

@@ -1,61 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage mod_menu
*
* @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org>
* @copyright (C) 2025 Jonathan Miler || Moko Consulting <https://mokoconsulting.tech>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\Utilities\ArrayHelper;
$attributes = [];
if ($item->anchor_title) {
$attributes['title'] = $item->anchor_title;
}
$attributes['class'] = 'mod-menu__heading nav-header';
$attributes['class'] .= $item->anchor_css ? ' ' . $item->anchor_css : null;
$linktype = $item->title;
if ($item->menu_icon) {
// The link is an icon
if ($itemParams->get('menu_text', 1)) {
// If the link text is to be displayed, the icon is added with aria-hidden
$linktype = '<span class="p-2 ' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
} else {
// If the icon itself is the link, it needs a visually hidden text
$linktype = '<span class="p-2 ' . $item->menu_icon . '" aria-hidden="true"></span><span class="visually-hidden">' . $item->title . '</span>';
}
} elseif ($item->menu_image) {
// The link is an image, maybe with an own class
$image_attributes = [];
if ($item->menu_image_css) {
$image_attributes['class'] = $item->menu_image_css;
}
$linktype = HTMLHelper::_('image', $item->menu_image, $item->title, $image_attributes);
if ($itemParams->get('menu_text', 1)) {
$linktype .= '<span class="image-title">' . $item->title . '</span>';
}
}
if ($showAll && $item->deeper) {
$attributes['class'] .= ' mm-collapsed mm-toggler mm-toggler-nolink';
$attributes['aria-haspopup'] = 'true';
$attributes['aria-expanded'] = 'false';
echo '<button ' . ArrayHelper::toString($attributes) . '>' . $linktype . '</button>';
} else {
echo '<span ' . ArrayHelper::toString($attributes) . '>' . $linktype . '</span>';
}

View File

@@ -1,61 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage mod_menu
*
* @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org>
* @copyright (C) 2025 Jonathan Miler || Moko Consulting <https://mokoconsulting.tech>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\Utilities\ArrayHelper;
$attributes = [];
if ($item->anchor_title) {
$attributes['title'] = $item->anchor_title;
}
$attributes['class'] = 'mod-menu__separator separator';
$attributes['class'] .= $item->anchor_css ? ' ' . $item->anchor_css : null;
$linktype = $item->title;
if ($item->menu_icon) {
// The link is an icon
if ($itemParams->get('menu_text', 1)) {
// If the link text is to be displayed, the icon is added with aria-hidden
$linktype = '<span class="p-2 ' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
} else {
// If the icon itself is the link, it needs a visually hidden text
$linktype = '<span class="p-2 ' . $item->menu_icon . '" aria-hidden="true"></span><span class="visually-hidden">' . $item->title . '</span>';
}
} elseif ($item->menu_image) {
// The link is an image, maybe with an own class
$image_attributes = [];
if ($item->menu_image_css) {
$image_attributes['class'] = $item->menu_image_css;
}
$linktype = HTMLHelper::_('image', $item->menu_image, $item->title, $image_attributes);
if ($itemParams->get('menu_text', 1)) {
$linktype .= '<span class="image-title">' . $item->title . '</span>';
}
}
if ($showAll && $item->deeper) {
$attributes['class'] .= ' mm-collapsed mm-toggler mm-toggler-nolink';
$attributes['aria-haspopup'] = 'true';
$attributes['aria-expanded'] = 'false';
echo '<button ' . ArrayHelper::toString($attributes) . '>' . $linktype . '</button>';
} else {
echo '<span ' . ArrayHelper::toString($attributes) . '>' . $linktype . '</span>';
}

View File

@@ -1,76 +0,0 @@
<?php
/**
* @package Joomla.Site
* @subpackage mod_menu
*
* @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org>
* @copyright (C) 2025 Jonathan Miler || Moko Consulting <https://mokoconsulting.tech>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\Filter\OutputFilter;
use Joomla\CMS\HTML\HTMLHelper;
$attributes = [];
if ($item->anchor_title) {
$attributes['title'] = $item->anchor_title;
}
if ($item->anchor_css) {
$attributes['class'] = $item->anchor_css;
}
if ($item->anchor_rel) {
$attributes['rel'] = $item->anchor_rel;
}
$linktype = $item->title;
if ($item->menu_icon) {
// The link is an icon
if ($itemParams->get('menu_text', 1)) {
// If the link text is to be displayed, the icon is added with aria-hidden
$linktype = '<span class="p-2 ' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
} else {
// If the icon itself is the link, it needs a visually hidden text
$linktype = '<span class="p-2 ' . $item->menu_icon . '" aria-hidden="true"></span><span class="visually-hidden">' . $item->title . '</span>';
}
} elseif ($item->menu_image) {
// The link is an image, maybe with an own class
$image_attributes = [];
if ($item->menu_image_css) {
$image_attributes['class'] = $item->menu_image_css;
}
$linktype = HTMLHelper::_('image', $item->menu_image, $item->title, $image_attributes);
if ($itemParams->get('menu_text', 1)) {
$linktype .= '<span class="image-title">' . $item->title . '</span>';
}
}
if ($item->browserNav == 1) {
$attributes['target'] = '_blank';
$attributes['rel'] = 'noopener noreferrer';
if ($item->anchor_rel == 'nofollow') {
$attributes['rel'] .= ' nofollow';
}
} elseif ($item->browserNav == 2) {
$options = 'toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,resizable=yes,' . $params->get('window_open');
$attributes['onclick'] = "window.open(this.href, 'targetWindow', '" . $options . "'); return false;";
}
echo HTMLHelper::link(OutputFilter::ampReplace(htmlspecialchars($item->flink, ENT_COMPAT, 'UTF-8', false)), $linktype, $attributes);
if ($showAll && $item->deeper) {
echo '<button class="mm-collapsed mm-toggler mm-toggler-link" aria-haspopup="true" aria-expanded="false" aria-label="' . $item->title . '"></button>';
}

View File

@@ -1,81 +0,0 @@
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
This file is part of a Moko Consulting project.
SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
DEFGROUP: Joomla.Templates.Site
INGROUP: Moko-Cassiopeia
FILE: index.html
BRIEF: Security redirect page to block folder access and forward to site root.
-->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Redirecting…</title>
<!-- Search engines: do not index this placeholder redirect page -->
<meta name="robots" content="noindex, nofollow, noarchive" />
<!-- Instant redirect fallback even if JavaScript is disabled -->
<meta http-equiv="refresh" content="0; url=/" />
<!-- Canonical root reference -->
<link rel="canonical" href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
(function redirectToRoot() {
// Configuration object with safe defaults.
var opts = {
fallbackPath: "/", // string: fallback destination if origin is unavailable
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
behavior: "replace" // enum: "replace" | "assign"
};
// Determine absolute origin in all mainstream browsers.
var origin = (typeof location.origin === "string" && location.origin)
|| (location.protocol + "//" + location.host);
// Final destination: absolute root of the current site, or fallback path.
var destination = origin ? origin + "/" : opts.fallbackPath;
function go() {
if (opts.behavior === "assign") {
location.assign(destination);
} else {
location.replace(destination);
}
}
// Execute redirect, optionally after a short delay.
if (opts.delayMs > 0) {
setTimeout(go, opts.delayMs);
} else {
go();
}
})();
</script>
<!--
Secondary meta-refresh for no-JS environments is already set above.
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
-->
<noscript>
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
<style>
html, body { height:100%; }
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
.msg { opacity: .75; text-align: center; }
</style>
</noscript>
</head>
<body>
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
</body>
</html>

View File

@@ -1,81 +0,0 @@
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
This file is part of a Moko Consulting project.
SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
DEFGROUP: Joomla.Templates.Site
INGROUP: Moko-Cassiopeia
FILE: index.html
BRIEF: Security redirect page to block folder access and forward to site root.
-->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Redirecting…</title>
<!-- Search engines: do not index this placeholder redirect page -->
<meta name="robots" content="noindex, nofollow, noarchive" />
<!-- Instant redirect fallback even if JavaScript is disabled -->
<meta http-equiv="refresh" content="0; url=/" />
<!-- Canonical root reference -->
<link rel="canonical" href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
(function redirectToRoot() {
// Configuration object with safe defaults.
var opts = {
fallbackPath: "/", // string: fallback destination if origin is unavailable
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
behavior: "replace" // enum: "replace" | "assign"
};
// Determine absolute origin in all mainstream browsers.
var origin = (typeof location.origin === "string" && location.origin)
|| (location.protocol + "//" + location.host);
// Final destination: absolute root of the current site, or fallback path.
var destination = origin ? origin + "/" : opts.fallbackPath;
function go() {
if (opts.behavior === "assign") {
location.assign(destination);
} else {
location.replace(destination);
}
}
// Execute redirect, optionally after a short delay.
if (opts.delayMs > 0) {
setTimeout(go, opts.delayMs);
} else {
go();
}
})();
</script>
<!--
Secondary meta-refresh for no-JS environments is already set above.
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
-->
<noscript>
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
<style>
html, body { height:100%; }
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
.msg { opacity: .75; text-align: center; }
</style>
</noscript>
</head>
<body>
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
</body>
</html>

View File

@@ -7,11 +7,11 @@
# FILE INFORMATION
DEFGROUP: Joomla.Template.Site
INGROUP: Moko-Cassiopeia
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
PATH: ./templates/moko-cassiopeia/index.php
VERSION: 03.06.00
BRIEF: Main template index file for Moko-Cassiopeia rendering site layout
INGROUP: MokoCassiopeia
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
PATH: ./templates/mokocassiopeia/index.php
VERSION: 03.06.02
BRIEF: Main template index file for MokoCassiopeia rendering site layout
*/
@@ -84,7 +84,7 @@ $final = $pageTitle !== ''
$this->setTitle($final);
// Template/Media path
$templatePath = 'media/templates/site/moko-cassiopeia';
$templatePath = 'media/templates/site/mokocassiopeia';
// Core template CSS
$wa->useStyle('template.base'); // css/template.css
@@ -510,7 +510,7 @@ $wa->useStyle('template.user'); // css/user.css
</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'); ?>">
<a href="#top" id="back-top" class="back-to-top-link" aria-label="<?php echo Text::_('TPL_MOKOCASSIOPEIA_BACKTOTOP'); ?>">
<span class="icon-arrow-up icon-fw" aria-hidden="true"></span>
</a>
<?php endif; ?>

View File

@@ -1,222 +1,235 @@
{
"$schema": "https://developer.joomla.org/schemas/json-schema/web_assets.json",
"name": "moko-cassiopeia",
"description": "Moko-Cassiopeia template assets",
"name": "mokocassiopeia",
"description": "MokoCassiopeia template assets",
"license": "GPL-3.0-or-later",
"x-header": {
"copyright_year": 2025,
"author": "Jonathan Miller",
"owner": "Moko Consulting",
"contact": "hello@mokoconsulting.tech",
"project": "Moko-Cassiopeia Template",
"project": "MokoCassiopeia Template",
"spdx_license": "GPL-3.0-or-later",
"notice": "This file is part of a Moko Consulting project.",
"disclaimer": "This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/.",
"repo": "https://github.com/mokoconsulting-tech/moko-cassiopeia",
"repo": "https://github.com/mokoconsulting-tech/MokoCassiopeia",
"file_information": {
"defgroup": "Joomla.Template.Site",
"ingroup": "Moko-Cassiopeia.Template.Assets",
"path": "./media/templates/site/moko-cassiopeia/joomla.asset.json",
"version": "03.06.00",
"brief": "Joomla asset registry for Moko-Cassiopeia"
"ingroup": "MokoCassiopeia.Template.Assets",
"path": "./media/templates/site/mokocassiopeia/joomla.asset.json",
"version": "03.06.02",
"brief": "Joomla asset registry for MokoCassiopeia"
}
},
"assets": [
{
"name": "template.base",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/css/template.css",
"uri": "media/templates/site/mokocassiopeia/css/template.css",
"attributes": {"media": "all"}
},
{
"name": "template.base.min",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/css/template.min.css",
"uri": "media/templates/site/mokocassiopeia/css/template.min.css",
"attributes": {"media": "all"}
},
{
"name": "template.user",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/css/user.css",
"uri": "media/templates/site/mokocassiopeia/css/user.css",
"attributes": {"media": "all"}
},
{
"name": "template.user.min",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/css/user.min.css",
"uri": "media/templates/site/mokocassiopeia/css/user.min.css",
"attributes": {"media": "all"}
},
{
"name": "template.editor",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/css/editor.css",
"uri": "media/templates/site/mokocassiopeia/css/editor.css",
"attributes": {"media": "all"}
},
{
"name": "template.editor.min",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/css/editor.min.css",
"uri": "media/templates/site/mokocassiopeia/css/editor.min.css",
"attributes": {"media": "all"}
},
{
"name": "template.light.colors_standard",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/css/colors/light/colors_standard.css",
"uri": "media/templates/site/mokocassiopeia/css/colors/light/colors_standard.css",
"attributes": {"media": "all"}
},
{
"name": "template.light.colors_standard.min",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/css/colors/light/colors_standard.min.css",
"uri": "media/templates/site/mokocassiopeia/css/colors/light/colors_standard.min.css",
"attributes": {"media": "all"}
},
{
"name": "template.light.colors_alternative",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/css/colors/light/colors_alternative.css",
"uri": "media/templates/site/mokocassiopeia/css/colors/light/colors_alternative.css",
"attributes": {"media": "all"}
},
{
"name": "template.light.colors_alternative.min",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/css/colors/light/colors_alternative.min.css",
"uri": "media/templates/site/mokocassiopeia/css/colors/light/colors_alternative.min.css",
"attributes": {"media": "all"}
},
{
"name": "template.light.colors_custom",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/css/colors/light/colors_custom.css",
"uri": "media/templates/site/mokocassiopeia/css/colors/light/colors_custom.css",
"attributes": {"media": "all"}
},
{
"name": "template.light.colors_custom.min",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/css/colors/light/colors_custom.min.css",
"uri": "media/templates/site/mokocassiopeia/css/colors/light/colors_custom.min.css",
"attributes": {"media": "all"}
},
{
"name": "template.dark.colors_standard",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/css/colors/dark/colors_standard.css",
"uri": "media/templates/site/mokocassiopeia/css/colors/dark/colors_standard.css",
"attributes": {"media": "all"}
},
{
"name": "template.dark.colors_standard.min",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/css/colors/dark/colors_standard.min.css",
"uri": "media/templates/site/mokocassiopeia/css/colors/dark/colors_standard.min.css",
"attributes": {"media": "all"}
},
{
"name": "template.dark.colors_alternative",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/css/colors/dark/colors_alternative.css",
"uri": "media/templates/site/mokocassiopeia/css/colors/dark/colors_alternative.css",
"attributes": {"media": "all"}
},
{
"name": "template.dark.colors_alternative.min",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/css/colors/dark/colors_alternative.min.css"
"uri": "media/templates/site/mokocassiopeia/css/colors/dark/colors_alternative.min.css"
},
{
"name": "template.dark.colors_custom",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/css/colors/dark/colors_custom.css",
"uri": "media/templates/site/mokocassiopeia/css/colors/dark/colors_custom.css",
"attributes": {"media": "all"}
},
{
"name": "template.dark.colors_custom.min",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/css/colors/dark/colors_custom.min.css",
"uri": "media/templates/site/mokocassiopeia/css/colors/dark/colors_custom.min.css",
"attributes": {"media": "all"}
},
{
"name": "template.js",
"type": "script",
"uri": "media/templates/site/moko-cassiopeia/js/template.js",
"uri": "media/templates/site/mokocassiopeia/js/template.js",
"attributes": {"defer": true}
},
{
"name": "template.js.min",
"type": "script",
"uri": "media/templates/site/moko-cassiopeia/js/template.min.js",
"uri": "media/templates/site/mokocassiopeia/js/template.min.js",
"attributes": {"defer": true}
},
{
"name": "gtm.js",
"type": "script",
"uri": "media/templates/site/moko-cassiopeia/js/gtm.js",
"uri": "media/templates/site/mokocassiopeia/js/gtm.js",
"attributes": {"defer": true}
},
{
"name": "gtm.min.js",
"type": "script",
"uri": "media/templates/site/moko-cassiopeia/js/gtm.min.js",
"uri": "media/templates/site/mokocassiopeia/js/gtm.min.js",
"attributes": {"defer": true}
},
{
"name": "vendor.fa7free.all",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/vendor/fa7free/css/all.css"
"uri": "media/templates/site/mokocassiopeia/vendor/fa7free/css/all.css"
},
{
"name": "vendor.fa7free.all.min",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/vendor/fa7free/css/all.min.css",
"uri": "media/templates/site/mokocassiopeia/vendor/fa7free/css/all.min.css",
"attributes": {"media": "all"}
},
{
"name": "vendor.fa7free.brands",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/vendor/fa7free/css/brands.css",
"uri": "media/templates/site/mokocassiopeia/vendor/fa7free/css/brands.css",
"attributes": {"media": "all"}
},
{
"name": "vendor.fa7free.brands.min",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/vendor/fa7free/css/brands.min.css",
"uri": "media/templates/site/mokocassiopeia/vendor/fa7free/css/brands.min.css",
"attributes": {"media": "all"}
},
{
"name": "vendor.fa7free.fontawesome",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/vendor/fa7free/css/fontawesome.css",
"uri": "media/templates/site/mokocassiopeia/vendor/fa7free/css/fontawesome.css",
"attributes": {"media": "all"}
},
{
"name": "vendor.fa7free.fontawesome.min",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/vendor/fa7free/css/fontawesome.min.css",
"uri": "media/templates/site/mokocassiopeia/vendor/fa7free/css/fontawesome.min.css",
"attributes": {"media": "all"}
},
{
"name": "vendor.fa7free.regular",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/vendor/fa7free/css/regular.css",
"uri": "media/templates/site/mokocassiopeia/vendor/fa7free/css/regular.css",
"attributes": {"media": "all"}
},
{
"name": "vendor.fa7free.regular.min",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/vendor/fa7free/css/regular.min.css",
"uri": "media/templates/site/mokocassiopeia/vendor/fa7free/css/regular.min.css",
"attributes": {"media": "all"}
},
{
"name": "vendor.fa7free.solid",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/vendor/fa7free/css/solid.css",
"uri": "media/templates/site/mokocassiopeia/vendor/fa7free/css/solid.css",
"attributes": {"media": "all"}
},
{
"name": "vendor.fa7free.solid.min",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/vendor/fa7free/css/solid.min.css",
"uri": "media/templates/site/mokocassiopeia/vendor/fa7free/css/solid.min.css",
"attributes": {"media": "all"}
},
{
"name": "vendor.vm",
"type": "style",
"uri": "media/templates/site/moko-cassiopeia/css/vendor/vm.css",
"uri": "media/templates/site/mokocassiopeia/css/vendor/vm.css",
"attributes": {"media": "all"}
},
{
"name": "vendor.bootstrap-toc",
"type": "style",
"uri": "media/templates/site/mokocassiopeia/vendor/bootstrap-toc/bootstrap-toc.css",
"attributes": {"media": "all"}
},
{
"name": "vendor.bootstrap-toc.js",
"type": "script",
"uri": "media/templates/site/mokocassiopeia/vendor/bootstrap-toc/bootstrap-toc.js",
"dependencies": ["jquery"],
"attributes": {"defer": true}
}
]
}

View File

@@ -7,11 +7,11 @@
# FILE INFORMATION
DEFGROUP: Joomla.Template.Site
INGROUP: Moko-Cassiopeia
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
PATH: ./templates/moko-cassiopeia/offline.php
VERSION: 03.06.00
BRIEF: Offline page template file for Moko-Cassiopeia
INGROUP: MokoCassiopeia
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
PATH: ./templates/mokocassiopeia/offline.php
VERSION: 03.06.02
BRIEF: Offline page template file for MokoCassiopeia
*/
declare(strict_types=1);

View File

@@ -9,25 +9,25 @@
=========================================================================
FILE INFORMATION
DEFGROUP: Joomla
INGROUP: Moko-Cassiopeia
PATH: templates/moko-cassiopeia/templateDetails.xml
VERSION: 03.06.00
BRIEF: Template manifest XML file for Moko-Cassiopeia
INGROUP: MokoCassiopeia
PATH: templates/mokocassiopeia/templateDetails.xml
VERSION: 03.06.02
BRIEF: Template manifest XML file for MokoCassiopeia
=========================================================================
-->
<extension type="template" client="site" method="upgrade">
<updateservers>
<server type="extension" name="Moko-Cassiopeia Updates" priority="1">
<server type="extension" name="MokoCassiopeia Updates" priority="1">
https://mokoconsulting.tech/index.php?option=com_ars&amp;view=update&amp;task=stream&amp;format=xml&amp;id=1
</server>
</updateservers>
<name>moko-cassiopeia</name>
<version>03.06.00</version>
<name>mokocassiopeia</name>
<version>03.06.02</version>
<creationDate>2025-12-23</creationDate>
<author>Jonathan Miller || Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<copyright>(C)GNU General Public License Version 3 - 2025 Moko Consulting</copyright>
<description>TPL_MOKO-CASSIOPEIA_XML_DESCRIPTION</description>
<description>TPL_MOKOCASSIOPEIA_XML_DESCRIPTION</description>
<inheritable>1</inheritable>
<files>
<filename>component.php</filename>
@@ -39,9 +39,9 @@
<folder>html</folder>
</files>
<stylesheets>
<stylesheet>media/templates/site/moko-cassiopeia/css/editor.css</stylesheet>
<stylesheet>media/templates/site/mokocassiopeia/css/editor.css</stylesheet>
</stylesheets>
<media destination="templates/site/moko-cassiopeia" folder="media">
<media destination="templates/site/mokocassiopeia" folder="media">
<folder>js</folder>
<folder>css</folder>
<folder>images</folder>
@@ -73,13 +73,13 @@
<position>drawer-right</position>
</positions>
<languages folder="language">
<language tag="en-GB">en-GB/tpl_moko-cassiopeia.ini</language>
<language tag="en-US">en-US/tpl_moko-cassiopeia.ini</language>
<language tag="en-GB">en-GB/tpl_mokocassiopeia.ini</language>
<language tag="en-US">en-US/tpl_mokocassiopeia.ini</language>
</languages>
<languages folder="administrator/language">
<language tag="en-GB">en-GB/tpl_moko-cassiopeia.sys.ini</language>
<language tag="en-US">en-US/tpl_moko-cassiopeia.sys.ini</language>
<language tag="en-GB">en-GB/tpl_mokocassiopeia.sys.ini</language>
<language tag="en-US">en-US/tpl_mokocassiopeia.sys.ini</language>
</languages>
<config>
@@ -88,41 +88,41 @@
<!-- Advanced tab (non-theme/system options only) -->
<fieldset name="advanced">
<!--
<field name="developmentmode" type="radio" label="TPL_MOKO-CASSIOPEIA_DEVELOPMENTMODE_LABEL" description="TPL_MOKO-CASSIOPEIA_DEVELOPMENTMODE_DESC" default="1" layout="joomla.form.field.radio.switcher" filter="boolean">
<field name="developmentmode" type="radio" label="TPL_MOKOCASSIOPEIA_DEVELOPMENTMODE_LABEL" description="TPL_MOKOCASSIOPEIA_DEVELOPMENTMODE_DESC" default="1" layout="joomla.form.field.radio.switcher" filter="boolean">
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
-->
<field name="fluidContainer" type="radio" layout="joomla.form.field.radio.switcher" default="0" label="TPL_MOKO-CASSIOPEIA_FLUID_LABEL">
<option value="0">TPL_MOKO-CASSIOPEIA_STATIC</option>
<option value="1">TPL_MOKO-CASSIOPEIA_FLUID</option>
<field name="fluidContainer" type="radio" layout="joomla.form.field.radio.switcher" default="0" label="TPL_MOKOCASSIOPEIA_FLUID_LABEL">
<option value="0">TPL_MOKOCASSIOPEIA_STATIC</option>
<option value="1">TPL_MOKOCASSIOPEIA_FLUID</option>
</field>
</fieldset>
<!-- Google tab -->
<fieldset name="google">
<field name="googletagmanager" type="radio" label="TPL_MOKO-CASSIOPEIA_GOOGLETAGMANAGER_LABEL" description="TPL_MOKO-CASSIOPEIA_GOOGLETAGMANAGER_DESC" layout="joomla.form.field.radio.switcher" filter="boolean">
<field name="googletagmanager" type="radio" label="TPL_MOKOCASSIOPEIA_GOOGLETAGMANAGER_LABEL" description="TPL_MOKOCASSIOPEIA_GOOGLETAGMANAGER_DESC" layout="joomla.form.field.radio.switcher" filter="boolean">
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
<field name="googletagmanagerid" type="text" default="" label="TPL_MOKO-CASSIOPEIA_GOOGLETAGMANAGERID_LABEL" description="TPL_MOKO-CASSIOPEIA_GOOGLETAGMANAGERID_DESC" filter="string" showon="googletagmanager:1" />
<field name="googleanalytics" type="radio" label="TPL_MOKO-CASSIOPEIA_GOOGLEANALYTICS_LABEL" description="TPL_MOKO-CASSIOPEIA_GOOGLEANALYTICS_DESC" layout="joomla.form.field.radio.switcher" filter="boolean">
<field name="googletagmanagerid" type="text" default="" label="TPL_MOKOCASSIOPEIA_GOOGLETAGMANAGERID_LABEL" description="TPL_MOKOCASSIOPEIA_GOOGLETAGMANAGERID_DESC" filter="string" showon="googletagmanager:1" />
<field name="googleanalytics" type="radio" label="TPL_MOKOCASSIOPEIA_GOOGLEANALYTICS_LABEL" description="TPL_MOKOCASSIOPEIA_GOOGLEANALYTICS_DESC" layout="joomla.form.field.radio.switcher" filter="boolean">
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
<field name="googleanalyticsid" type="text" default="" label="TPL_MOKO-CASSIOPEIA_GOOGLEANALYTICSID_LABEL" description="TPL_MOKO-CASSIOPEIA_GOOGLEANALYTICSID_DESC" filter="string" showon="googleanalytics:1" />
<field name="googleanalyticsid" type="text" default="" label="TPL_MOKOCASSIOPEIA_GOOGLEANALYTICSID_LABEL" description="TPL_MOKOCASSIOPEIA_GOOGLEANALYTICSID_DESC" filter="string" showon="googleanalytics:1" />
</fieldset>
<!-- Custom Code tab -->
<fieldset name="custom_head" label="TPL_MOKO-CASSIOPEIA_CUSTOM_CODE_FIELDSET">
<field name="custom_head_start" type="textarea" default="" label="TPL_MOKO-CASSIOPEIA_CUSTOM_HEAD_START_LABEL" description="TPL_MOKO-CASSIOPEIA_CUSTOM_HEAD_START_DESC" filter="raw" />
<field name="custom_head_end" type="textarea" default="" label="TPL_MOKO-CASSIOPEIA_CUSTOM_HEAD_END_LABEL" description="TPL_MOKO-CASSIOPEIA_CUSTOM_HEAD_END_DESC" filter="raw" />
<fieldset name="custom_head" label="TPL_MOKOCASSIOPEIA_CUSTOM_CODE_FIELDSET">
<field name="custom_head_start" type="textarea" default="" label="TPL_MOKOCASSIOPEIA_CUSTOM_HEAD_START_LABEL" description="TPL_MOKOCASSIOPEIA_CUSTOM_HEAD_START_DESC" filter="raw" />
<field name="custom_head_end" type="textarea" default="" label="TPL_MOKOCASSIOPEIA_CUSTOM_HEAD_END_LABEL" description="TPL_MOKOCASSIOPEIA_CUSTOM_HEAD_END_DESC" filter="raw" />
</fieldset>
<!-- Drawers tab -->
<fieldset name="drawers">
<field name="drawerLeftIcon" type="text" default="fa-solid fa-chevron-right" label="TPL_MOKO-CASSIOPEIA_DRAWER_LEFT_ICON_LABEL" description="TPL_MOKO-CASSIOPEIA_DRAWER_LEFT_ICON_DESC" filter="string" />
<field name="drawerRightIcon" type="text" default="fa-solid fa-chevron-left" label="TPL_MOKO-CASSIOPEIA_DRAWER_RIGHT_ICON_LABEL" description="TPL_MOKO-CASSIOPEIA_DRAWER_RIGHT_ICON_DESC" filter="string" />
<field name="drawerLeftIcon" type="text" default="fa-solid fa-chevron-right" label="TPL_MOKOCASSIOPEIA_DRAWER_LEFT_ICON_LABEL" description="TPL_MOKOCASSIOPEIA_DRAWER_LEFT_ICON_DESC" filter="string" />
<field name="drawerRightIcon" type="text" default="fa-solid fa-chevron-left" label="TPL_MOKOCASSIOPEIA_DRAWER_RIGHT_ICON_LABEL" description="TPL_MOKOCASSIOPEIA_DRAWER_RIGHT_ICON_DESC" filter="string" />
</fieldset>
<!-- THEME TAB (all style/theme settings grouped) -->
@@ -175,49 +175,49 @@
<!-- Variables & Palettes -->
<field name="theme_sep_vars" type="spacer" label="Variables &amp; Palettes" hr="false" class="text fw-bold" />
<field name="colorLightName" type="list" label="TPL_MOKO-CASSIOPEIA_COLOR_LIGHT_NAME_LABEL" default="colors_standard">
<option value="colors_standard">TPL_MOKO-CASSIOPEIA_COLOR_NAME_STANDARD</option>
<option value="colors_alternative">TPL_MOKO-CASSIOPEIA_COLOR_NAME_ALTERNATIVE</option>
<option value="colors_custom">TPL_MOKO-CASSIOPEIA_COLOR_NAME_CUSTOM</option>
<field name="colorLightName" type="list" label="TPL_MOKOCASSIOPEIA_COLOR_LIGHT_NAME_LABEL" default="colors_standard">
<option value="colors_standard">TPL_MOKOCASSIOPEIA_COLOR_NAME_STANDARD</option>
<option value="colors_alternative">TPL_MOKOCASSIOPEIA_COLOR_NAME_ALTERNATIVE</option>
<option value="colors_custom">TPL_MOKOCASSIOPEIA_COLOR_NAME_CUSTOM</option>
</field>
<field name="colorDarkName" type="list" label="TPL_MOKO-CASSIOPEIA_COLOR_DARK_NAME_LABEL" default="colors_standard">
<option value="colors_standard">TPL_MOKO-CASSIOPEIA_COLOR_NAME_STANDARD</option>
<option value="colors_alternative">TPL_MOKO-CASSIOPEIA_COLOR_NAME_ALTERNATIVE</option>
<option value="colors_custom">TPL_MOKO-CASSIOPEIA_COLOR_NAME_CUSTOM</option>
<field name="colorDarkName" type="list" label="TPL_MOKOCASSIOPEIA_COLOR_DARK_NAME_LABEL" default="colors_standard">
<option value="colors_standard">TPL_MOKOCASSIOPEIA_COLOR_NAME_STANDARD</option>
<option value="colors_alternative">TPL_MOKOCASSIOPEIA_COLOR_NAME_ALTERNATIVE</option>
<option value="colors_custom">TPL_MOKOCASSIOPEIA_COLOR_NAME_CUSTOM</option>
</field>
<!-- Typography -->
<field name="theme_sep_typo" type="spacer" label="Typography" hr="false" class="text fw-bold" />
<field name="useFontScheme" type="groupedlist" label="TPL_MOKO-CASSIOPEIA_FONT_LABEL" default="0">
<field name="useFontScheme" type="groupedlist" label="TPL_MOKOCASSIOPEIA_FONT_LABEL" default="0">
<option value="0">JNONE</option>
<group label="TPL_MOKO-CASSIOPEIA_FONT_GROUP_LOCAL">
<option value="media/templates/site/moko-cassiopeia/css/global/fonts-local_roboto.css">Roboto (local)</option>
<group label="TPL_MOKOCASSIOPEIA_FONT_GROUP_LOCAL">
<option value="media/templates/site/mokocassiopeia/css/global/fonts-local_roboto.css">Roboto (local)</option>
</group>
<group label="TPL_MOKO-CASSIOPEIA_FONT_GROUP_WEB">
<group label="TPL_MOKOCASSIOPEIA_FONT_GROUP_WEB">
<option value="https://fonts.googleapis.com/css2?family=Fira+Sans:wght@100;300;400;700&amp;display=swap">Fira Sans (web)</option>
<option value="https://fonts.googleapis.com/css2?family=Noto+Sans:wght@100;300;400;700&amp;family=Roboto:wght@100;300;400;700&amp;display=swap">Roboto + Noto Sans (web)</option>
</group>
</field>
<field name="noteFontScheme" type="note" description="TPL_MOKO-CASSIOPEIA_FONT_NOTE_TEXT" class="alert alert-warning" />
<field name="noteFontScheme" type="note" description="TPL_MOKOCASSIOPEIA_FONT_NOTE_TEXT" class="alert alert-warning" />
<!-- Branding & Icons -->
<field name="theme_sep_brand" type="spacer" label="Branding &amp; Icons" hr="false" class="text fw-bold" />
<field name="brand" type="radio" label="TPL_MOKO-CASSIOPEIA_BRAND_LABEL" default="1" layout="joomla.form.field.radio.switcher" filter="boolean">
<field name="brand" type="radio" label="TPL_MOKOCASSIOPEIA_BRAND_LABEL" default="1" layout="joomla.form.field.radio.switcher" filter="boolean">
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
<field name="logoFile" type="media" default="media/templates/site/moko-cassiopeia/images/logo.svg" label="TPL_MOKO-CASSIOPEIA_LOGO_LABEL" showon="brand:1" />
<field name="siteTitle" type="text" default="" label="TPL_MOKO-CASSIOPEIA_TITLE" filter="string" showon="brand:1" />
<field name="siteDescription" type="text" default="" label="TPL_MOKO-CASSIOPEIA_TAGLINE_LABEL" description="TPL_MOKO-CASSIOPEIA_TAGLINE_DESC" filter="string" showon="brand:1" />
<field name="fA6KitCode" type="text" default="" label="TPL_MOKO-CASSIOPEIA_FA7KITCODE_LABEL" description="TPL_MOKO-CASSIOPEIA_FA7KITCODE_DESC" filter="string" />
<field name="logoFile" type="media" default="media/templates/site/mokocassiopeia/images/logo.svg" label="TPL_MOKOCASSIOPEIA_LOGO_LABEL" showon="brand:1" />
<field name="siteTitle" type="text" default="" label="TPL_MOKOCASSIOPEIA_TITLE" filter="string" showon="brand:1" />
<field name="siteDescription" type="text" default="" label="TPL_MOKOCASSIOPEIA_TAGLINE_LABEL" description="TPL_MOKOCASSIOPEIA_TAGLINE_DESC" filter="string" showon="brand:1" />
<field name="fA6KitCode" type="text" default="" label="TPL_MOKOCASSIOPEIA_FA7KITCODE_LABEL" description="TPL_MOKOCASSIOPEIA_FA7KITCODE_DESC" filter="string" />
<!-- Header & Navigation UI -->
<field name="theme_sep_header" type="spacer" label="Header &amp; Navigation" hr="false" class="text fw-bold" />
<field name="stickyHeader" type="radio" label="TPL_MOKO-CASSIOPEIA_STICKY_LABEL" layout="joomla.form.field.radio.switcher" default="0" filter="integer">
<field name="stickyHeader" type="radio" label="TPL_MOKOCASSIOPEIA_STICKY_LABEL" layout="joomla.form.field.radio.switcher" default="0" filter="integer">
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
<field name="backTop" type="radio" label="TPL_MOKO-CASSIOPEIA_BACKTOTOP_LABEL" layout="joomla.form.field.radio.switcher" default="0" filter="integer">
<field name="backTop" type="radio" label="TPL_MOKOCASSIOPEIA_BACKTOTOP_LABEL" layout="joomla.form.field.radio.switcher" default="0" filter="integer">
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>

View File

@@ -7,31 +7,31 @@
# FILE INFORMATION
DEFGROUP: Joomla.Template.Update
INGROUP: Moko-Cassiopeia
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
INGROUP: MokoCassiopeia
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
PATH: ./updates.xml
VERSION: 03.06.00
BRIEF: Update manifest XML file for Moko-Cassiopeia
VERSION: 03.06.02
BRIEF: Update manifest XML file for MokoCassiopeia
-->
<updates>
<update>
<name>Moko Cassiopeia</name>
<name>MokoCassiopeia</name>
<description>Moko Consultings site template based on Cassiopeia.</description>
<element>moko-cassiopeia</element>
<element>mokocassiopeia</element>
<type>template</type>
<client>site</client>
<version>03.06.00</version>
<version>03.06.02</version>
<creationDate>2025-12-12</creationDate>
<author>Jonathan Miller || Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<copyright>(C)GNU General Public License Version 3 - 2025 Moko Consulting</copyright>
<infourl title='Moko Cassiopeia'>https://github.com/mokoconsulting-tech/moko-cassiopeia</infourl>
<infourl title='MokoCassiopeia'>https://github.com/mokoconsulting-tech/MokoCassiopeia</infourl>
<downloads>
<downloadurl type='full' format='zip'>https://github.com/mokoconsulting-tech/moko-cassiopeia/releases/download/03.01.00/moko-cassiopeia-src-03.01.00.zip</downloadurl>
<downloadurl type='full' format='zip'>https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/03.01.00/mokocassiopeia-src-03.01.00.zip</downloadurl>
<sha256>sha256:858dd1a9a0aceecfe844a22d2a3557aea68687ae02363e04ff4c80bae0f29480</sha256>
</downloads>