chore: Sync MokoStandards workflows and configurations (#85)

* chore: update LICENSE from MokoStandards

* chore: add update.xml from MokoStandards

* chore: update phpstan.neon from MokoStandards

* chore: add Makefile from MokoStandards

* chore: update .gitignore from MokoStandards

* chore: add composer.json from MokoStandards

* chore: add .moko-standards from MokoStandards

* chore: update .github/copilot.yml from MokoStandards

* chore: update .github/copilot-instructions.md from MokoStandards

* chore: update .github/CLAUDE.md from MokoStandards

* chore: add .github/workflows/codeql-analysis.yml from MokoStandards

* chore: add .github/workflows/standards-compliance.yml from MokoStandards

* chore: add .github/workflows/enterprise-firewall-setup.yml from MokoStandards

* chore: add .github/workflows/deploy-dev.yml from MokoStandards

* chore: add .github/ISSUE_TEMPLATE/config.yml from MokoStandards

* chore: add .github/ISSUE_TEMPLATE/adr.md from MokoStandards

* chore: add .github/ISSUE_TEMPLATE/bug_report.md from MokoStandards

* chore: add .github/ISSUE_TEMPLATE/documentation.md from MokoStandards

* chore: add .github/ISSUE_TEMPLATE/enterprise_support.md from MokoStandards

* chore: add .github/ISSUE_TEMPLATE/feature_request.md from MokoStandards

* chore: add .github/ISSUE_TEMPLATE/firewall-request.md from MokoStandards

* chore: add .github/ISSUE_TEMPLATE/question.md from MokoStandards

* chore: add .github/ISSUE_TEMPLATE/request-license.md from MokoStandards

* chore: add .github/ISSUE_TEMPLATE/rfc.md from MokoStandards

* chore: add .github/ISSUE_TEMPLATE/security.md from MokoStandards

* chore: add .github/ISSUE_TEMPLATE/joomla_issue.md from MokoStandards
This commit was merged in pull request #85.
This commit is contained in:
2026-03-24 11:22:08 -05:00
committed by GitHub
parent 1d17fd9ae9
commit 1dcbfe1219
26 changed files with 6228 additions and 943 deletions

711
.github/CLAUDE.md vendored
View File

@@ -1,545 +1,274 @@
> [!IMPORTANT]
> **🔧 AI Self-Update Required on First Use**
>
> This file may contain unfilled `{{TOKEN}}` placeholders. The MokoStandards bulk-sync system
> replaces them automatically at sync time; if you can still see them, sync has not yet run for
> this repository — or this is a brand-new repo.
>
> **On your very first interaction in this repository, before answering any other question:**
>
> 1. Check whether any `{{TOKEN}}` placeholders remain in this file.
> 2. If they do, locate the values as described in the table below and replace every occurrence
> in **both** `.github/copilot-instructions.md` and `.github/CLAUDE.md`.
> 3. After replacing all tokens, **delete this entire notice block** (from `> [!IMPORTANT]`
> through the closing `> ---` line) in both files.
> 4. Tell the developer: *"I've self-updated the AI context files with this repository's
> values — all set."*
>
> | Placeholder | Where to find the value |
> |---|---|
> | `MokoCassiopeia` | The GitHub repository name (visible in the URL, `README.md` heading, or `git remote -v`) |
> | `https://github.com/mokoconsulting-tech/MokoCassiopeia` | Full GitHub URL, e.g. `https://github.com/mokoconsulting-tech/<repo-name>` |
> | `A modern enhancement layer for Joomlas Cassiopeia template. Moko-Cassiopeia adds Font Awesome 7, Bootstrap 5 helpers, an automatic Table of Contents (TOC) utility, and optional expansions including Google Tag Manager and Google Analytics (GA4) hooks.` | First paragraph of `README.md` body, or the GitHub repo description |
> | `{{EXTENSION_NAME}}` | The `<name>` element in `manifest.xml` at the repository root |
> | `{{EXTENSION_TYPE}}` | The `type` attribute of the `<extension>` tag in `manifest.xml` (`component`, `module`, `plugin`, or `template`) |
> | `{{EXTENSION_ELEMENT}}` | The `<element>` tag in `manifest.xml`, or the filename prefix (e.g. `com_myextension`, `mod_mymodule`) |
>
> ---
# What This Repo Is
MokoCassiopeia is a modern, lightweight Joomla template that extends Cassiopeia (Joomla's default template) with enhanced features while maintaining maximum upgrade compatibility. It is used by Joomla 4.4.x and 5.x site administrators who want Font Awesome 7, Bootstrap 5 helpers, dark mode theming, and table of contents features without breaking core Joomla upgrades. This is NOT a standalone theme framework, NOT a template builder, and NOT a general-purpose UI library—it is specifically a Joomla site template extension that must be installed via Joomla's Extension Manager. Repository: https://github.com/mokoconsulting-tech/MokoCassiopeia
**MokoCassiopeia** is a Moko Consulting **MokoWaaS** (Joomla) extension repository.
A modern enhancement layer for Joomlas Cassiopeia template. Moko-Cassiopeia adds Font Awesome 7, Bootstrap 5 helpers, an automatic Table of Contents (TOC) utility, and optional expansions including Google Tag Manager and Google Analytics (GA4) hooks.
Extension name: **{{EXTENSION_NAME}}**
Extension type: **{{EXTENSION_TYPE}}** (`{{EXTENSION_ELEMENT}}`)
Repository URL: https://github.com/mokoconsulting-tech/MokoCassiopeia
This repository is governed by [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards) — the single source of truth for coding standards, file-header policies, GitHub Actions workflows, and Terraform configuration templates across all Moko Consulting repositories.
---
# Repo Structure
```
MokoCassiopeia/
├── .github/ # GitHub workflows (CI, testing, release), Copilot instructions
├── docs/ # Comprehensive documentation (guides, philosophy, CSS reference)
├── scripts/ # Build scripts (build-release.sh)
├── src/ # MAIN WORKING DIRECTORY: template source files
│ ├── index.php # Main template rendering file
│ ├── templateDetails.xml # Joomla manifest (metadata, params, files)
── joomla.asset.json # Web Asset Manager registration
│ ├── language/ # Frontend language files (en-GB, en-US .ini and .sys.ini)
│ ├── html/ # Alternative layout overrides (never replace defaults!)
── media/ # CSS, JS, images, fonts, vendor libraries
├── templates/ # Color scheme templates for client customization
├── tests/ # Codeception acceptance and unit tests
├── phpcs.xml # PHP_CodeSniffer configuration (PSR-12 + Joomla standards)
── phpstan.neon # PHPStan static analysis configuration
├── manifest.xml # Joomla installer manifest (root — required)
├── update.xml # Update server manifest (root — required)
├── site/ # Frontend (site) code
│ ├── controller.php
│ ├── controllers/
│ ├── models/
── views/
├── admin/ # Backend (admin) code
│ ├── controller.php
── controllers/
│ ├── models/
│ ├── views/
│ └── sql/
── language/ # Language INI files
├── media/ # CSS, JS, images
├── docs/ # Technical documentation
├── tests/ # Test suite
├── .github/
│ ├── workflows/ # CI/CD workflows (synced from MokoStandards)
│ ├── copilot-instructions.md
│ └── CLAUDE.md # This file
├── README.md # Version source of truth
├── CHANGELOG.md
├── CONTRIBUTING.md
└── LICENSE # GPL-3.0-or-later
```
---
# Primary Language
**PHP** (≥ 7.4) is the primary language for this Joomla extension. YAML uses 2-space indentation. All other text files use tabs per `.editorconfig`.
---
# Version Management
**`README.md` is the single source of truth for the repository version.**
- **Bump the patch version on every PR** — increment `XX.YY.ZZ` (e.g. `01.02.03``01.02.04`) in `README.md` before opening the PR; the `sync-version-on-merge` workflow propagates it to all `FILE INFORMATION` headers automatically on merge.
- Version format is zero-padded semver: `XX.YY.ZZ` (e.g. `01.02.03`).
- Never hardcode a version number in body text — use the badge or FILE INFORMATION header only.
### Joomla Version Alignment
Three files must **always have the same version**:
| File | Where the version lives |
|------|------------------------|
| `README.md` | `FILE INFORMATION` block + badge |
| `manifest.xml` | `<version>` tag |
| `update.xml` | `<version>` in the most recent `<update>` block |
The `make release` command / release workflow syncs all three automatically.
---
# update.xml — Required in Repo Root
`update.xml` is the Joomla update server manifest. It allows Joomla installations to check for new versions of this extension via:
```xml
<!-- In manifest.xml -->
<updateservers>
<server type="extension" priority="1" name="{{EXTENSION_NAME}}">
https://github.com/mokoconsulting-tech/MokoCassiopeia/raw/main/update.xml
</server>
</updateservers>
```
**Rules:**
- Every release prepends a new `<update>` block at the top — older entries are preserved.
- `<version>` in `update.xml` must exactly match `<version>` in `manifest.xml` and `README.md`.
- `<downloadurl>` must be a publicly accessible GitHub Releases asset URL.
- `<targetplatform version="4\.[0-9]+">` — backslash is literal (Joomla regex syntax).
Example `update.xml` entry for a new release:
```xml
<updates>
<update>
<name>{{EXTENSION_NAME}}</name>
<description>MokoCassiopeia</description>
<element>{{EXTENSION_ELEMENT}}</element>
<type>{{EXTENSION_TYPE}}</type>
<version>01.02.04</version>
<infourl title="Release Information">https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/tag/01.02.04</infourl>
<downloads>
<downloadurl type="full" format="zip">
https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/01.02.04/{{EXTENSION_ELEMENT}}-01.02.04.zip
</downloadurl>
</downloads>
<targetplatform name="joomla" version="4\.[0-9]+" />
<php_minimum>7.4</php_minimum>
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
</update>
</updates>
```
---
# File Header Requirements
## Header Format
All source files MUST include this standardized copyright header:
### PHP Files (Full Header)
Every new file **must** have a copyright header as its first content. JSON files, binary files, generated files, and third-party files are exempt.
**PHP:**
```php
<?php
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
This file is part of a Moko Consulting project.
SPDX-License-Identifier: GPL-3.0-or-later
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
# FILE INFORMATION
DEFGROUP: Joomla.Template.Site
INGROUP: MokoCassiopeia.Template
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
PATH: ./templates/mokocassiopeia/filename.php
VERSION: XX.YY.ZZ
BRIEF: Brief description of file purpose
*
* This file is part of a Moko Consulting project.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* FILE INFORMATION
* DEFGROUP: MokoCassiopeia.{{EXTENSION_TYPE}}
* INGROUP: MokoCassiopeia
* REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
* PATH: /site/controllers/item.php
* VERSION: XX.YY.ZZ
* BRIEF: One-line description of file purpose
*/
defined('_JEXEC') or die;
```
### JavaScript Files
**Markdown / YAML / Shell / XML:** Use the appropriate comment syntax with the same fields.
```javascript
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
This file is part of a Moko Consulting project.
SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
DEFGROUP: Joomla.Template.Site
INGROUP: MokoCassiopeia
PATH: ./media/templates/site/mokocassiopeia/js/filename.js
VERSION: XX.YY.ZZ
BRIEF: Brief description of file purpose
*/
(function (win, doc) {
"use strict";
// Implementation
})(window, document);
```
### CSS Files
```css
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
This file is part of a Moko Consulting project.
SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
DEFGROUP: Joomla.Template.Site
INGROUP: MokoCassiopeia.Styles
PATH: ./media/templates/site/mokocassiopeia/css/filename.css
VERSION: XX.YY.ZZ
BRIEF: Brief description of file purpose
*/
```
### Markdown Documentation
```markdown
<!--
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
DEFGROUP: Joomla.Template.Site
INGROUP: MokoCassiopeia.Documentation
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
FILE: docs/FILENAME.md
VERSION: XX.YY.ZZ
BRIEF: Brief description
-->
```
## FILE INFORMATION Block Fields
- **DEFGROUP**: Top-level group (always `Joomla.Template.Site`)
- **INGROUP**: Subgroup (e.g., `MokoCassiopeia.Template`, `MokoCassiopeia.Documentation`)
- **REPO**: GitHub repository URL (required in docs, optional in code)
- **PATH** or **FILE**: Relative path from repository root
- **VERSION**: Semantic version `XX.YY.ZZ`
- **BRIEF**: One-line description of file purpose
## Exempt Files
These file types DO NOT require headers:
- `joomla.asset.json` and other JSON data files (metadata in `x-header` field instead)
- Binary files (images, fonts, compiled assets)
- Third-party vendor libraries in `src/media/vendor/`
- Generated files (minified CSS/JS with `.min.` in name)
- Empty placeholder files
## When to Use Full vs Minimal Header
- **Full header with GPL notice**: Required in all PHP files (use example above)
- **Minimal header without GPL notice**: JavaScript and CSS files (shorter version shown above)
- **Markdown format**: All documentation files in `docs/`
---
# Coding Standards
## Indentation
From `.editorconfig`:
- **Default**: Tabs with 2-space visual width
- **PHP**: Tabs (PSR-12 uses tabs, Joomla uses tabs)
- **JavaScript**: Tabs with 2-space visual width
- **CSS**: Tabs with 2-space visual width
- **JSON/YAML**: Tabs with 2-space visual width
- **Markdown**: Spaces (for compatibility), trim_trailing_whitespace = false
- **Makefiles**: Always tabs
## Line Length
From `phpcs.xml`:
- **General**: No strict limit (Generic.Files.LineLength excluded)
- **Practical guideline**: Keep lines under 120 characters when reasonable
- **Long lines accepted for**: URLs, array definitions, Joomla HTML helpers
## Naming Conventions
### PHP (Joomla Standards + PSR-12)
| Context | Convention | Example |
|---------|-----------|---------|
| PHP class | `PascalCase` | `ItemModel` |
| PHP method / function | `camelCase` | `getItems()` |
| PHP variable | `$snake_case` | `$item_id` |
| PHP constant | `UPPER_SNAKE_CASE` | `MAX_ITEMS` |
| PHP class file | `PascalCase.php` | `ItemModel.php` |
| YAML workflow | `kebab-case.yml` | `ci-joomla.yml` |
| Markdown doc | `kebab-case.md` | `installation-guide.md` |
- **Classes**: `PascalCase` (e.g., `TemplateHelper`)
- **Methods**: `camelCase` (e.g., `getThemeOptions()`)
- **Variables**: `$camelCase` or `$snake_case` (Joomla prefers snake_case for local vars)
- **Constants**: `UPPER_SNAKE_CASE` (e.g., `THEME_LIGHT_MODE`)
- **Files**: Lowercase with hyphens for layouts (e.g., `mobile.php`, `toc-left.php`)
- **Private properties**: Prefix with underscore (e.g., `$_privateData`)
## Commit Messages
### JavaScript (ES6+)
Format: `<type>(<scope>): <subject>` — imperative, lower-case subject, no trailing period.
- **Functions**: `camelCase` (e.g., `applyTheme()`)
- **Constants**: `UPPER_SNAKE_CASE` or `camelCase` (e.g., `storageKey`)
- **Classes**: `PascalCase` (e.g., `ThemeController`)
- **Files**: Lowercase with hyphens (e.g., `template.js`, `menu-metismenu.js`)
Valid types: `feat` · `fix` · `docs` · `chore` · `ci` · `refactor` · `style` · `test` · `perf` · `revert` · `build`
### CSS
## Branch Naming
- **Classes**: Kebab-case (e.g., `.theme-toggle`, `.fab-button`)
- **IDs**: Kebab-case (e.g., `#main-content`)
- **CSS Variables**: Kebab-case with double hyphen prefix (e.g., `--color-primary`, `--body-bg`)
- **Files**: Lowercase with dots and descriptive names (e.g., `light.standard.css`, `dark.custom.css`, `light.custom.css`)
Format: `<prefix>/<MAJOR.MINOR.PATCH>[/description]`
### General File Naming
Approved prefixes: `dev/` · `rc/` · `version/` · `patch/` · `copilot/` · `dependabot/`
- **Alternative layouts**: Descriptive name + `.php` (e.g., `mobile.php`, `mainmenu.php`, `toc-left.php`)
- **NEVER use**: `default.php` (replaces core layout—FORBIDDEN)
- **Scripts**: Lowercase with hyphens (e.g., `build-release.sh`)
---
## Primary Language
# GitHub Actions — Token Usage
- **PHP**: All new template logic, Joomla integration code
- **JavaScript**: All new client-side functionality (ES6+ syntax)
- **CSS**: All new styling (CSS variables for theming)
- **Bash**: Build and automation scripts (`.sh` files)
Every workflow must use **`secrets.GH_TOKEN`** (the org-level Personal Access Token).
# Language-Specific Requirements
```yaml
# ✅ Correct
- uses: actions/checkout@v4
with:
token: ${{ secrets.GH_TOKEN }}
## PHP
### Type Hints
Required for all function signatures in PHP 8.0+ code:
```php
public function getThemeOption(string $key, mixed $default = null): mixed
{
return $this->params->get($key, $default);
}
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
```
### Docstring Format
Use DocBlocks for all classes, methods, and functions:
```php
/**
* Apply theme settings to document
*
* @param string $theme Theme name (light|dark)
* @param object $params Template parameters
*
* @return void
*
* @since 3.6.0
*/
public function applyTheme(string $theme, object $params): void
{
// Implementation
}
```yaml
# ❌ Wrong — never use these
token: ${{ github.token }}
token: ${{ secrets.GITHUB_TOKEN }}
```
Required DocBlock sections:
- Brief description (first line)
- `@param` for each parameter (type, name, description)
- `@return` for return value
- `@since` for version introduced
- `@throws` if exceptions are thrown
---
### Script Structure
# Keeping Documentation Current
All PHP files must follow this pattern:
| Change type | Documentation to update |
|-------------|------------------------|
| New or renamed PHP class/method | PHPDoc block; `docs/api/` entry |
| New or changed `manifest.xml` | Sync version to `update.xml` and `README.md` |
| New release | Prepend `<update>` to `update.xml`; update `CHANGELOG.md`; bump `README.md` |
| New or changed workflow | `docs/workflows/<workflow-name>.md` |
| Any modified file | Update the `VERSION` field in that file's `FILE INFORMATION` block |
| **Every PR** | **Bump the patch version** — increment `XX.YY.ZZ` in `README.md`; `sync-version-on-merge` propagates it |
```php
<?php
/* [Copyright header - see above] */
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
// Other imports
/** @var Joomla\CMS\Document\HtmlDocument $this */
// Constants (if needed)
const THEME_LIGHT = 'light';
// Implementation
$app = Factory::getApplication();
```
### Error Handling
- Use `defined('_JEXEC') or die;` at the start of EVERY PHP file
- Catch exceptions with specific types: `catch (RuntimeException $e)`
- Log errors using Joomla's logging: `JLog::add($message, JLog::ERROR)`
- Never expose internal paths or sensitive data in error messages
- Use `try-catch` blocks for database operations
## JavaScript
### Type Hints
Use JSDoc type annotations:
```javascript
/**
* Apply theme to document root
* @param {"light"|"dark"} theme - Theme name
* @returns {void}
*/
function applyTheme(theme) {
root.setAttribute("data-bs-theme", theme);
}
```
### Docstring Format
```javascript
/**
* Toggle theme between light and dark modes
*
* Toggles the theme attribute on the root element and updates
* localStorage with the new preference.
*
* @param {Event} event - Click event from toggle button
* @returns {void}
*/
function toggleTheme(event) {
// Implementation
}
```
### Script Structure
```javascript
/* [Copyright header - see above] */
(function (win, doc) {
"use strict";
// Constants
const STORAGE_KEY = "theme";
const root = doc.documentElement;
// Private functions
function applyTheme(theme) {
// Implementation
}
// Public API (if needed)
win.TemplateAPI = {
applyTheme: applyTheme
};
})(window, document);
```
### Error Handling
- Always use `"use strict";` at the top of IIFE
- Wrap localStorage access in try-catch (may throw in private mode)
- Use `console.error()` for logging errors (not `console.log()`)
- Validate function parameters at the start
- Return early on error conditions
## CSS
No specific requirements beyond standard CSS syntax. Use CSS variables for theming (see `docs/CSS_VARIABLES.md`).
# Commit Message Format
No `.gitmessage` file exists. Follow this format:
```
Brief summary (50 chars or less)
Detailed explanation if needed:
- What changed
- Why it changed
- Impact of the change
Refs: #issue-number (if applicable)
```
**Good examples:**
```
Fix language file installation paths in templateDetails.xml
- Remove folder attribute from <languages> section
- Update paths to be relative from package root
- Ensures proper installation to JOOMLA_ROOT/language/
Refs: #42
```
```
Add dark mode color variables for custom themes
- Add --color-primary-dark, --color-secondary-dark
- Update theme toggle to respect custom colors
- Document new variables in CSS_VARIABLES.md
```
**Bad examples:**
```
Update files
```
```
WIP
```
```
Fixed bug
```
## Commit Rules
- Keep subject line under 50 characters
- Use imperative mood ("Add feature" not "Added feature")
- Capitalize first letter
- No period at end of subject
- Blank line between subject and body
- Wrap body at 72 characters
- Explain WHAT and WHY, not HOW (code shows HOW)
# Running Validation
Before committing, run these commands from repository root:
```bash
# 1. PHP CodeSniffer (code style)
phpcs --standard=phpcs.xml src/
# 2. PHPStan (static analysis)
phpstan analyse --configuration=phpstan.neon
# 3. XML validation (template manifest)
xmllint --noout src/templateDetails.xml
# 4. Check file headers (if script exists)
# ./scripts/validate-headers.sh
# 5. Run tests (if Codeception installed)
codecept run
```
If you don't have tools installed:
```bash
# Install PHP quality tools globally
composer global require "squizlabs/php_codesniffer:^3.0" --with-all-dependencies
composer global require "phpstan/phpstan:^1.0" --with-all-dependencies
# Install Codeception
composer global require "codeception/codeception" --with-all-dependencies
```
**Note:** CI runs these automatically on push, but running locally saves time.
# Contribution Workflow
1. **Fork** the repository on GitHub
2. **Clone** your fork locally: `git clone https://github.com/YOUR-USERNAME/MokoCassiopeia.git`
3. **Create branch** from current development branch: `git checkout -b feature/your-feature-name`
- Branch naming: `feature/description`, `fix/issue-description`, `docs/topic`
4. **Make changes** in the `src/` directory (this is the working copy)
5. **Run validation** (see commands above)
6. **Commit changes** with descriptive messages
7. **Push to your fork**: `git push origin feature/your-feature-name`
8. **Open Pull Request** on GitHub targeting the appropriate base branch
## Branch Strategy
- **main**: Stable releases only (protected)
- **version/XX.YY**: Version-specific branches (protected)
- **dev/XX.YY.ZZ**: Active development branches
- **rc/XX.YY.ZZ**: Release candidate branches
- **feature/**, **fix/**, **docs/**: Working branches
## Merge Strategy
- Pull requests use **squash merge** to main/version branches
- Commits go to development branches first, then promoted through rc → version → main
- NEVER commit directly to protected branches (main, version/*)
# PR Checklist
Before opening a pull request:
- [ ] Code follows Joomla coding standards (PSR-12 + Joomla)
- [ ] All files have proper copyright headers
- [ ] PHP files have `defined('_JEXEC') or die;` at top
- [ ] No `default.php` layout files (use alternative layout names)
- [ ] Language files: metadata in `.sys.ini`, runtime strings in `.ini`
- [ ] Assets registered in `joomla.asset.json` (not inline in template)
- [ ] CSS uses CSS variables for colors (supports light/dark themes)
- [ ] PHP CodeSniffer passes: `phpcs --standard=phpcs.xml src/`
- [ ] PHPStan passes: `phpstan analyse --configuration=phpstan.neon`
- [ ] Tested in Joomla 4.4.x AND 5.x
- [ ] Tested in light mode AND dark mode
- [ ] Documentation updated if adding features
- [ ] No hardcoded absolute paths (use Joomla path constants)
- [ ] No credentials, API keys, or secrets in code
- [ ] Version numbers NOT added to revision history tables
- [ ] Alternative layouts documented with activation instructions
---
# What NOT to Do
## Absolutely Forbidden
- **Never commit directly to `main`** — all changes go through a PR.
- **Never hardcode version numbers** in body text — update `README.md` and let automation propagate.
- **Never let `manifest.xml`, `update.xml`, and `README.md` versions diverge.**
- **Never skip the FILE INFORMATION block** on a new source file.
- **Never use bare `catch (\Throwable $e) {}`** — always log or re-throw.
- **Never mix tabs and spaces** within a file — follow `.editorconfig`.
- **Never use `github.token` or `secrets.GITHUB_TOKEN` in workflows** — always use `secrets.GH_TOKEN`.
- **Never remove `defined('_JEXEC') or die;`** from web-accessible PHP files.
-**NEVER create `default.php` layout files** - This replaces core layouts and breaks upgrades. Use alternative layout names like `mobile.php`, `mainmenu.php`
-**NEVER commit without copyright headers** - All source files must have proper headers
-**NEVER add `folder` attribute to `<languages>` section in templateDetails.xml** - Language file paths must be relative from package root
-**NEVER put template metadata in `.ini` files** - Template name and description belong ONLY in `.sys.ini` files
-**NEVER hardcode absolute paths** - Use Joomla path constants (`JPATH_ROOT`, `JPATH_SITE`, etc.)
-**NEVER commit credentials or API keys** - Use Joomla's configuration management
-**NEVER modify vendor libraries** - Third-party code in `src/media/vendor/` is immutable
-**NEVER remove `defined('_JEXEC') or die;`** - This is a critical security check
---
## Strong Discouragements
# PR Checklist
- ⚠️ Avoid committing without running `phpcs` and `phpstan`
- ⚠️ Avoid adding version numbers to revision history tables (use VERSION metadata instead)
- ⚠️ Avoid inline styles or scripts (register assets in `joomla.asset.json`)
- ⚠️ Avoid hardcoded color values (use CSS variables: `--color-primary`, etc.)
- ⚠️ Avoid direct commits to `main` or `version/*` branches (use PRs)
- ⚠️ Avoid changing more than one concern per commit
- ⚠️ Avoid long lines when reasonable (aim for 120 chars)
Before opening a PR, verify:
## Common Mistakes
- [ ] Patch version bumped in `README.md` (e.g. `01.02.03``01.02.04`)
- [ ] If this is a release: `manifest.xml` version updated; `update.xml` updated with new entry
- [ ] FILE INFORMATION headers updated in modified files
- [ ] CHANGELOG.md updated
- [ ] Tests pass
- Using spaces instead of tabs (check `.editorconfig`)
- Forgetting `SPDX-License-Identifier: GPL-3.0-or-later` in headers
- Mixing frontend strings (.ini) and metadata strings (.sys.ini)
- Creating layout overrides that replace defaults instead of providing alternatives
- Not testing in both Joomla 4.x and 5.x
- Not testing light/dark mode theme switching
---
# Key Policy Documents
# Key Policy Documents (MokoStandards)
Must-read before contributing:
1. **[OVERRIDE_PHILOSOPHY.md](./docs/OVERRIDE_PHILOSOPHY.md)** - CRITICAL: Explains why we NEVER replace default layouts and how alternative layouts work
2. **[QUICK_START.md](./docs/QUICK_START.md)** - 5-minute setup guide for first-time contributors
3. **[JOOMLA_DEVELOPMENT.md](./docs/JOOMLA_DEVELOPMENT.md)** - Complete development guide: testing, quality checks, deployment
4. **[WORKFLOW_GUIDE.md](./docs/WORKFLOW_GUIDE.md)** - Git workflow, branching strategy, CI/CD pipelines
5. **[CSS_VARIABLES.md](./docs/CSS_VARIABLES.md)** - Complete reference for theme customization
6. **[CONTRIBUTING.md](./CONTRIBUTING.md)** - Formal contribution guidelines and governance
7. **[SECURITY.md](./SECURITY.md)** - Security reporting procedures (never report publicly)
8. **[MODULE_OVERRIDES.md](./docs/MODULE_OVERRIDES.md)** - Documentation of all alternative layouts and activation instructions
## Related Documentation
- **README.md** - Overview, features, installation, configuration
- **CHANGELOG.md** - Version history and release notes
- **GOVERNANCE.md** - Project governance model and decision-making
- **CODE_OF_CONDUCT.md** - Community standards and expectations
| Document | Purpose |
|----------|---------|
| [file-header-standards.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/file-header-standards.md) | Copyright-header rules for every file type |
| [coding-style-guide.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/coding-style-guide.md) | Naming and formatting conventions |
| [branching-strategy.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/branching-strategy.md) | Branch naming, hierarchy, and release workflow |
| [merge-strategy.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/merge-strategy.md) | Squash-merge policy and PR conventions |
| [changelog-standards.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/changelog-standards.md) | How and when to update CHANGELOG.md |
| [joomla-development-guide.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/guide/waas/joomla-development-guide.md) | MokoWaaS Joomla extension development guide |

125
.github/ISSUE_TEMPLATE/adr.md vendored Normal file
View File

@@ -0,0 +1,125 @@
---
name: Architecture Decision Record (ADR)
about: Propose or document an architectural decision
title: '[ADR] '
labels: 'architecture, decision'
assignees: ''
---
<!--
SPDX-License-Identifier: GPL-3.0-or-later
Copyright (C) 2024-2026 Moko Consulting Tech
File: .github/ISSUE_TEMPLATE/adr.md
Description: Issue template for Architecture Decision Records
Project: .github-private
Author: Moko Consulting Tech
Version: 03.02.00
Revision History:
- 2026-01-04: Added MokoStandards compliant header with copyright, file info, and metadata
- 2026-03-11: Version bump to 03.02.00 to match MokoStandards
- 2024: Initial creation
-->
## ADR Number
ADR-XXXX
## Status
- [ ] Proposed
- [ ] Accepted
- [ ] Deprecated
- [ ] Superseded by ADR-XXXX
## Context
Describe the issue or problem that motivates this decision.
## Decision
State the architecture decision and provide rationale.
## Consequences
### Positive
- List positive consequences
### Negative
- List negative consequences or trade-offs
### Neutral
- List neutral aspects
## Alternatives Considered
### Alternative 1
- Description
- Pros
- Cons
- Why not chosen
### Alternative 2
- Description
- Pros
- Cons
- Why not chosen
## Implementation Plan
1. Step 1
2. Step 2
3. Step 3
## Stakeholders
- **Decision Makers**: @user1, @user2
- **Consulted**: @user3, @user4
- **Informed**: team-name
## Technical Details
### Architecture Diagram
```
[Add diagram or link]
```
### Dependencies
- Dependency 1
- Dependency 2
### Impact Analysis
- **Performance**: [Impact description]
- **Security**: [Impact description]
- **Scalability**: [Impact description]
- **Maintainability**: [Impact description]
## Testing Strategy
- [ ] Unit tests
- [ ] Integration tests
- [ ] Performance tests
- [ ] Security tests
## Documentation
- [ ] Architecture documentation updated
- [ ] API documentation updated
- [ ] Developer guide updated
- [ ] Runbook created
## Migration Path
Describe how to migrate from current state to new architecture.
## Rollback Plan
Describe how to rollback if issues occur.
## Timeline
- **Proposal Date**:
- **Decision Date**:
- **Implementation Start**:
- **Expected Completion**:
## References
- Related ADRs:
- External resources:
- RFCs:
## Review Checklist
- [ ] Aligns with enterprise architecture principles
- [ ] Security implications reviewed
- [ ] Performance implications reviewed
- [ ] Cost implications reviewed
- [ ] Compliance requirements met
- [ ] Team consensus achieved

63
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,63 @@
---
name: Bug Report
about: Report a bug or issue with the project
title: '[BUG] '
labels: 'bug'
assignees: ''
---
<!--
SPDX-License-Identifier: GPL-3.0-or-later
Copyright (C) 2024-2026 Moko Consulting Tech
File: .github/ISSUE_TEMPLATE/bug_report.md
Description: Issue template for bug reports
Project: .github-private
Author: Moko Consulting Tech
Version: 03.02.00
Revision History:
- 2026-01-04: Added MokoStandards compliant header with copyright, file info, and metadata
- 2026-03-11: Version bump to 03.02.00 to match MokoStandards
- 2024: Initial creation
-->
## Bug Description
A clear and concise description of what the bug is.
## Steps to Reproduce
1. Go to '...'
2. Click on '...'
3. Scroll down to '...'
4. See error
## Expected Behavior
A clear and concise description of what you expected to happen.
## Actual Behavior
A clear and concise description of what actually happened.
## Screenshots
If applicable, add screenshots to help explain your problem.
## Environment
- **Project**: [e.g., MokoDoliTools, moko-cassiopeia]
- **Version**: [e.g., 1.2.3]
- **Platform**: [e.g., Dolibarr 18.0, Joomla 5.0]
- **PHP Version**: [e.g., 8.1]
- **Database**: [e.g., MySQL 8.0, PostgreSQL 14]
- **Browser** (if applicable): [e.g., Chrome 120, Firefox 121]
- **OS**: [e.g., Ubuntu 22.04, Windows 11]
## Additional Context
Add any other context about the problem here.
## Possible Solution
If you have suggestions on how to fix the issue, please describe them here.
## Checklist
- [ ] I have searched for similar issues before creating this one
- [ ] I have provided all the requested information
- [ ] I have tested this on the latest stable version
- [ ] I have checked the documentation and couldn't find a solution

18
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,18 @@
---
blank_issues_enabled: true
contact_links:
- name: 💼 Enterprise Support
url: https://mokoconsulting.tech/enterprise
about: Enterprise-level support and consultation services
- name: 💬 Ask a Question
url: https://mokoconsulting.tech/
about: Get help or ask questions through our website
- name: 📚 MokoStandards Documentation
url: https://github.com/mokoconsulting-tech/MokoStandards
about: View our coding standards and best practices
- name: 🔒 Report a Security Vulnerability
url: https://github.com/mokoconsulting-tech/.github-private/security/advisories/new
about: Report security vulnerabilities privately (for critical issues)
- name: 💡 Community Discussions
url: https://github.com/orgs/mokoconsulting-tech/discussions
about: Join community discussions and Q&A

67
.github/ISSUE_TEMPLATE/documentation.md vendored Normal file
View File

@@ -0,0 +1,67 @@
---
name: Documentation Issue
about: Report an issue with documentation
title: '[DOCS] '
labels: 'documentation'
assignees: ''
---
<!--
SPDX-License-Identifier: GPL-3.0-or-later
Copyright (C) 2024-2026 Moko Consulting Tech
File: .github/ISSUE_TEMPLATE/documentation.md
Description: Issue template for documentation-related issues
Project: .github-private
Author: Moko Consulting Tech
Version: 03.02.00
Revision History:
- 2026-01-04: Added MokoStandards compliant header with copyright, file info, and metadata
- 2026-03-11: Version bump to 03.02.00 to match MokoStandards
- 2024: Initial creation
-->
## Documentation Issue
**Location**:
<!-- Specify the file, page, or section with the issue -->
## Issue Type
<!-- Mark the relevant option with an "x" -->
- [ ] Typo or grammar error
- [ ] Outdated information
- [ ] Missing documentation
- [ ] Unclear explanation
- [ ] Broken links
- [ ] Missing examples
- [ ] Other (specify below)
## Description
<!-- Clearly describe the documentation issue -->
## Current Content
<!-- Quote or describe the current documentation (if applicable) -->
```
Current text here
```
## Suggested Improvement
<!-- Provide your suggestion for how to improve the documentation -->
```
Suggested text here
```
## Additional Context
<!-- Add any other context, screenshots, or references -->
## Standards Alignment
- [ ] Follows MokoStandards documentation guidelines
- [ ] Uses en_US/en_GB localization
- [ ] Includes proper SPDX headers where applicable
## Checklist
- [ ] I have searched for similar documentation issues
- [ ] I have provided a clear description
- [ ] I have suggested an improvement (if applicable)

View File

@@ -0,0 +1,100 @@
---
name: Enterprise Support Request
about: Request enterprise-level support or consultation
title: '[ENTERPRISE] '
labels: 'enterprise, support'
assignees: ''
---
<!--
SPDX-License-Identifier: GPL-3.0-or-later
Copyright (C) 2024-2026 Moko Consulting Tech
File: .github/ISSUE_TEMPLATE/enterprise_support.md
Description: Issue template for enterprise support requests
Project: .github-private
Author: Moko Consulting Tech
Version: 03.02.00
Revision History:
- 2026-01-04: Added MokoStandards compliant header with copyright, file info, and metadata
- 2026-03-11: Version bump to 03.02.00 to match MokoStandards
- 2024: Initial creation
-->
## Support Request Type
- [ ] Critical Production Issue
- [ ] Performance Optimization
- [ ] Security Audit
- [ ] Architecture Review
- [ ] Custom Development
- [ ] Migration Support
- [ ] Training & Onboarding
- [ ] Other (please specify)
## Priority Level
- [ ] P0 - Critical (Production Down)
- [ ] P1 - High (Major Feature Broken)
- [ ] P2 - Medium (Non-Critical Issue)
- [ ] P3 - Low (Enhancement/Question)
## Organization Details
- **Company Name**:
- **Contact Person**:
- **Email**:
- **Phone** (for P0/P1 issues):
- **Timezone**:
## Issue Description
Provide a clear and detailed description of your request or issue.
## Business Impact
Describe the impact on your business operations:
- Number of users affected:
- Revenue impact (if applicable):
- Deadline/SLA requirements:
## Environment Details
- **Deployment Type**: [On-Premise / Cloud / Hybrid]
- **Platform**: [Joomla / Dolibarr / Custom]
- **Version**:
- **Infrastructure**: [AWS / Azure / GCP / Other]
- **Scale**: [Users / Transactions / Data Volume]
## Current Configuration
```yaml
# Paste relevant configuration (sanitize sensitive data)
```
## Logs and Diagnostics
```
# Paste relevant logs (sanitize sensitive data)
```
## Attempted Solutions
Describe any troubleshooting steps already taken.
## Expected Resolution
Describe your expected outcome or resolution.
## Additional Resources
- **Documentation Links**:
- **Related Issues**:
- **Screenshots/Videos**:
## Enterprise SLA
- [ ] Standard Support (initial response within 13 weeks)
- [ ] Premium Support (initial response within 5 business days)
- [ ] Critical Support (initial response within 72 hours)
- [ ] Custom SLA (specify):
## Compliance Requirements
- [ ] GDPR
- [ ] HIPAA
- [ ] SOC 2
- [ ] ISO 27001
- [ ] Other (specify):
---
**Note**: Enterprise support requests require an active support contract. If you don't have one, please contact us at enterprise@mokoconsulting.tech

View File

@@ -0,0 +1,66 @@
---
name: Feature Request
about: Suggest a new feature or enhancement
title: '[FEATURE] '
labels: 'enhancement'
assignees: ''
---
<!--
SPDX-License-Identifier: GPL-3.0-or-later
Copyright (C) 2024-2026 Moko Consulting Tech
File: .github/ISSUE_TEMPLATE/feature_request.md
Description: Issue template for feature requests
Project: .github-private
Author: Moko Consulting Tech
Version: 03.02.00
Revision History:
- 2026-01-04: Added MokoStandards compliant header with copyright, file info, and metadata
- 2026-03-11: Version bump to 03.02.00 to match MokoStandards
- 2024: Initial creation
-->
## Feature Description
A clear and concise description of the feature you'd like to see.
## Problem or Use Case
Describe the problem this feature would solve or the use case it addresses.
Ex. I'm always frustrated when [...]
## Proposed Solution
A clear and concise description of what you want to happen.
## Alternative Solutions
A clear and concise description of any alternative solutions or features you've considered.
## Benefits
Describe how this feature would benefit users:
- Who would use this feature?
- What problems does it solve?
- What value does it add?
## Implementation Details (Optional)
If you have ideas about how this could be implemented, share them here:
- Technical approach
- Files/components that might need changes
- Any concerns or challenges you foresee
## Additional Context
Add any other context, mockups, or screenshots about the feature request here.
## Relevant Standards
Does this relate to any standards in [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards)?
- [ ] Accessibility (WCAG 2.1 AA)
- [ ] Localization (en_US/en_GB)
- [ ] Security best practices
- [ ] Code quality standards
- [ ] Other: [specify]
## Checklist
- [ ] I have searched for similar feature requests before creating this one
- [ ] I have clearly described the use case and benefits
- [ ] I have considered alternative solutions
- [ ] This feature aligns with the project's goals and scope

View File

@@ -0,0 +1,203 @@
---
name: Firewall Request
about: Request firewall rule changes or access to external resources
title: '[FIREWALL] [Resource Name] - [Brief Description]'
labels: ['firewall-request', 'infrastructure', 'security']
assignees: []
---
<!--
SPDX-License-Identifier: GPL-3.0-or-later
Copyright (C) 2024-2026 Moko Consulting Tech
File: .github/ISSUE_TEMPLATE/firewall-request.md
Description: Issue template for firewall rule change and access requests
Project: .github-private
Author: Moko Consulting Tech
Version: 03.02.00
Revision History:
- 2026-03-11: Added SPDX header and version to match MokoStandards 03.02.00
-->
## Firewall Request
### Request Type
- [ ] Allow outbound access to external service/API
- [ ] Allow inbound access from external source
- [ ] Modify existing firewall rule
- [ ] Remove/revoke firewall rule
- [ ] Other (specify):
### Resource Information
**Service/Domain Name**:
**IP Address(es)**:
**Port(s)**:
**Protocol**:
- [ ] HTTP (80)
- [ ] HTTPS (443)
- [ ] SSH (22)
- [ ] FTP (21)
- [ ] SFTP (22)
- [ ] Custom (specify): _______________
### Requestor Information
**Name**:
**GitHub Username**: @
**Email**: @mokoconsulting.tech
**Team/Department**:
**Manager**: @
### Business Justification
**Why is this access needed?**
**Which project(s) require this access?**
**What functionality will break without this access?**
**Is there an alternative solution?**
- [ ] Yes (explain):
- [ ] No
### Security Considerations
**Data Classification**:
- [ ] Public
- [ ] Internal
- [ ] Confidential
- [ ] Restricted
**Sensitive Data Transmission**:
- [ ] No sensitive data will be transmitted
- [ ] Sensitive data will be transmitted (encryption required)
- [ ] Authentication credentials will be transmitted (secure storage required)
**Third-Party Service**:
- [ ] This is a trusted/verified third-party service
- [ ] This is a new/unverified service (security review required)
**Service Documentation**:
(Provide link to service documentation or API specs)
### Access Scope
**Affected Systems**:
- [ ] Development environment only
- [ ] Staging environment only
- [ ] Production environment
- [ ] All environments
**Access Duration**:
- [ ] Permanent (ongoing business need)
- [ ] Temporary (specify end date): _______________
- [ ] Testing only (specify duration): _______________
### Technical Details
**Source System(s)**:
(Which internal systems need access?)
**Destination System(s)**:
(Which external systems need to be accessed?)
**Expected Traffic Volume**:
(e.g., requests per hour/day)
**Traffic Pattern**:
- [ ] Continuous
- [ ] Periodic (specify frequency): _______________
- [ ] On-demand/manual
- [ ] Scheduled (specify schedule): _______________
### Testing Requirements
**Pre-Production Testing**:
- [ ] Request includes dev/staging access for testing
- [ ] Testing can be done with production access only
- [ ] No testing required (modify existing rule)
**Testing Plan**:
**Rollback Plan**:
(What happens if access needs to be revoked?)
### Compliance & Audit
**Compliance Requirements**:
- [ ] GDPR considerations
- [ ] SOC 2 compliance required
- [ ] PCI DSS considerations
- [ ] Other regulatory requirements: _______________
- [ ] No specific compliance requirements
**Audit/Logging Requirements**:
- [ ] Standard logging sufficient
- [ ] Enhanced logging/monitoring required
- [ ] Real-time alerting required
### Urgency
- [ ] Critical (production down, immediate access needed)
- [ ] High (needed within 24 hours)
- [ ] Normal (needed within 1 week)
- [ ] Low priority (needed within 1 month)
**If critical/high urgency, explain why:**
### Approvals
**Manager Approval**:
- [ ] Manager has been notified and approves this request
**Security Team Review Required**:
- [ ] Yes (new external service, sensitive data)
- [ ] No (minor change, established service)
### Additional Information
**Related Documentation**:
(Links to relevant docs, RFCs, tickets, etc.)
**Dependencies**:
(Other systems or changes this depends on)
**Comments/Questions**:
---
## For Infrastructure/Security Team Use Only
**Do not edit below this line**
### Security Review
- [ ] Security team review completed
- [ ] Risk assessment: Low / Medium / High
- [ ] Encryption required: Yes / No
- [ ] VPN required: Yes / No
- [ ] Additional security controls: _______________
**Reviewed By**: @_______________
**Review Date**: _______________
**Review Notes**:
### Implementation
- [ ] Firewall rule created/modified
- [ ] Rule tested in dev/staging
- [ ] Rule deployed to production
- [ ] Monitoring/alerting configured
- [ ] Documentation updated
**Firewall Rule ID**: _______________
**Implementation Date**: _______________
**Implemented By**: @_______________
**Configuration Details**:
```
Source:
Destination:
Port/Protocol:
Action: Allow/Deny
```
### Verification
- [ ] Requestor confirmed access working
- [ ] Logs reviewed (no anomalies)
- [ ] Security scan completed (if applicable)
**Verification Date**: _______________
**Verified By**: @_______________
### Notes

102
.github/ISSUE_TEMPLATE/joomla_issue.md vendored Normal file
View File

@@ -0,0 +1,102 @@
---
name: Joomla Extension Issue
about: Report an issue with a Joomla extension
title: '[JOOMLA] '
labels: 'joomla'
assignees: ''
---
<!--
SPDX-License-Identifier: GPL-3.0-or-later
Copyright (C) 2024-2026 Moko Consulting Tech
File: .github/ISSUE_TEMPLATE/joomla_issue.md
Description: Issue template for Joomla extension-specific issues
Project: .github-private
Author: Moko Consulting Tech
Version: 03.02.00
Revision History:
- 2026-01-04: Added MokoStandards compliant header with copyright, file info, and metadata
- 2026-03-11: Version bump to 03.02.00 to match MokoStandards
- 2024: Initial creation
-->
## Issue Type
- [ ] Component Issue
- [ ] Module Issue
- [ ] Plugin Issue
- [ ] Template Issue
## Extension Details
- **Extension Name**: [e.g., moko-cassiopeia]
- **Extension Version**: [e.g., 1.2.3]
- **Extension Type**: [Component / Module / Plugin / Template]
## Joomla Environment
- **Joomla Version**: [e.g., 4.4.0, 5.0.0]
- **PHP Version**: [e.g., 8.1.0]
- **Database**: [MySQL / PostgreSQL / MariaDB]
- **Database Version**: [e.g., 8.0]
- **Server**: [Apache / Nginx / IIS]
- **Hosting**: [Shared / VPS / Dedicated / Cloud]
## Issue Description
Provide a clear and detailed description of the issue.
## Steps to Reproduce
1. Go to '...'
2. Click on '...'
3. Configure '...'
4. See error
## Expected Behavior
What you expected to happen.
## Actual Behavior
What actually happened.
## Error Messages
```
# Paste any error messages from Joomla error logs
# Location: administrator/logs/error.php
```
## Browser Console Errors
```javascript
// Paste any JavaScript console errors (F12 in browser)
```
## Screenshots
Add screenshots to help explain the issue.
## Configuration
```ini
# Paste extension configuration (sanitize sensitive data)
```
## Installed Extensions
List other installed extensions that might conflict:
- Extension 1 (version)
- Extension 2 (version)
## Template Overrides
- [ ] Using template overrides
- [ ] Custom CSS
- [ ] Custom JavaScript
## Additional Context
- **Multilingual Site**: [Yes / No]
- **Cache Enabled**: [Yes / No]
- **Debug Mode**: [Yes / No]
- **SEF URLs**: [Yes / No]
## Checklist
- [ ] I have cleared Joomla cache
- [ ] I have disabled other extensions to test for conflicts
- [ ] I have checked Joomla error logs
- [ ] I have tested with a default Joomla template
- [ ] I have checked browser console for JavaScript errors
- [ ] I have searched for similar issues
- [ ] I am using a supported Joomla version

86
.github/ISSUE_TEMPLATE/question.md vendored Normal file
View File

@@ -0,0 +1,86 @@
---
name: Question
about: Ask a question about usage, features, or best practices
title: '[QUESTION] '
labels: ['question']
assignees: []
---
<!--
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
SPDX-License-Identifier: GPL-3.0-or-later
-->
## Question
**Your question:**
## Context
**What are you trying to accomplish?**
**What have you already tried?**
**Category**:
- [ ] Script usage
- [ ] Configuration
- [ ] Workflow setup
- [ ] Documentation interpretation
- [ ] Best practices
- [ ] Integration
- [ ] Other: __________
## Environment (if relevant)
**Your setup**:
- Operating System:
- Version:
## What You've Researched
**Documentation reviewed**:
- [ ] README.md
- [ ] Project documentation
- [ ] Other (specify): __________
**Similar issues/questions found**:
- #
- #
## Expected Outcome
**What result are you hoping for?**
## Code/Configuration Samples
**Relevant code or configuration** (if applicable):
```bash
# Your code here
```
## Additional Context
**Any other relevant information:**
**Screenshots** (if helpful):
## Urgency
- [ ] Urgent (blocking work)
- [ ] Normal (can work on other things meanwhile)
- [ ] Low priority (just curious)
## Checklist
- [ ] I have searched existing issues and discussions
- [ ] I have reviewed relevant documentation
- [ ] I have provided sufficient context
- [ ] I have included code/configuration samples if relevant
- [ ] This is a genuine question (not a bug report or feature request)

View File

@@ -0,0 +1,120 @@
---
name: License Request
about: Request an organization license for Sublime Text
title: '[LICENSE REQUEST] Sublime Text - [Your Name]'
labels: ['license-request', 'admin']
assignees: []
---
<!--
SPDX-License-Identifier: GPL-3.0-or-later
Copyright (C) 2024-2026 Moko Consulting Tech
File: .github/ISSUE_TEMPLATE/request-license.md
Description: Issue template for organization software license requests
Project: .github-private
Author: Moko Consulting Tech
Version: 03.02.00
Revision History:
- 2026-03-11: Added SPDX header and version to match MokoStandards 03.02.00
-->
## License Request
### Tool Information
**Tool Name**: Sublime Text
**License Type Requested**: Organization Pool
**Personal Purchase**:
- [ ] I prefer to purchase my own license ($99 USD - recommended, immediate access)
- [ ] I prefer an organization license (1-2 business days, organization use only)
- [ ] I have already purchased my own license (registration only for support)
### Requestor Information
**Name**:
**GitHub Username**: @
**Email**: @mokoconsulting.tech
**Team/Department**:
**Manager**: @
### Justification
**Why do you need this license?**
**Primary use case**:
- [ ] Remote development (SFTP to servers)
- [ ] Local development
- [ ] Code review
- [ ] Documentation editing
- [ ] Other (specify):
**Which projects/repositories will you work on?**
**Have you evaluated the free trial?**
- [ ] Yes, I've used the trial and Sublime Text meets my needs
- [ ] No, requesting license before trial
**Alternative tools considered**:
- [ ] VS Code (free alternative)
- [ ] Vim/Neovim (free, terminal-based)
- [ ] Other: _______________
### Platform
- [ ] Windows
- [ ] macOS
- [ ] Linux (distribution: ________)
### Urgency
- [ ] Urgent (needed within 24 hours - please justify)
- [ ] Normal (1-2 business days)
- [ ] Low priority (when available)
**If urgent, please explain why:**
### SFTP Plugin
**Note**: Sublime SFTP plugin ($16 USD) is a **separate personal purchase** and is NOT provided by the organization.
- [ ] I understand SFTP plugin requires separate personal purchase
- [ ] I have already purchased SFTP plugin
- [ ] I will purchase SFTP plugin if needed for my work
- [ ] I don't need SFTP plugin (local development only)
### Acknowledgments
- [ ] I have read the License Management Policy (/docs/github-private/LICENSE_MANAGEMENT.md)
- [ ] I understand organization licenses are for work use only
- [ ] I understand organization licenses must be returned upon leaving
- [ ] I understand personal purchases ($99) are an alternative with lifetime access
- [ ] I understand SFTP plugin ($16) requires separate personal purchase
- [ ] I agree to the terms of use
### Additional Information
**Expected daily usage hours**: _____ hours/day
**Duration of need**:
- [ ] Permanent (ongoing role)
- [ ] Temporary project (_____ months)
- [ ] Trial/Evaluation (_____ weeks)
**Comments/Questions**:
---
## For Admin Use Only
**Do not edit below this line**
- [ ] Manager approval received (@manager-username)
- [ ] License available in pool (current: __/20)
- [ ] License type confirmed (Organization / Personal registration)
- [ ] License key sent via encrypted email
- [ ] Activation confirmed by user
- [ ] Added to license tracking sheet
- [ ] User notified of SFTP plugin requirement
**License Key ID**: _____________
**Date Issued**: _____________
**Issued By**: @_____________
**Notes**:

141
.github/ISSUE_TEMPLATE/rfc.md vendored Normal file
View File

@@ -0,0 +1,141 @@
---
name: Request for Comments (RFC)
about: Propose a significant change for community discussion
title: '[RFC] '
labels: 'rfc, discussion'
assignees: ''
---
<!--
SPDX-License-Identifier: GPL-3.0-or-later
Copyright (C) 2024-2026 Moko Consulting Tech
File: .github/ISSUE_TEMPLATE/rfc.md
Description: Issue template for Request for Comments proposals
Project: .github-private
Author: Moko Consulting Tech
Version: 03.02.00
Revision History:
- 2026-01-04: Added MokoStandards compliant header with copyright, file info, and metadata
- 2026-03-11: Version bump to 03.02.00 to match MokoStandards
- 2024: Initial creation
-->
## RFC Summary
One-paragraph summary of the proposal.
## Motivation
Why are we doing this? What use cases does it support? What is the expected outcome?
## Detailed Design
### Overview
Provide a detailed explanation of the proposed change.
### API Changes (if applicable)
```php
// Before
function oldApi($param1) { }
// After
function newApi($param1, $param2) { }
```
### User Experience Changes
Describe how users will interact with this change.
### Implementation Approach
High-level implementation strategy.
## Drawbacks
Why should we *not* do this?
## Alternatives
What other designs have been considered? What is the impact of not doing this?
### Alternative 1
- Description
- Trade-offs
### Alternative 2
- Description
- Trade-offs
## Adoption Strategy
How will existing users adopt this? Is this a breaking change?
### Migration Guide
```bash
# Steps to migrate
```
### Deprecation Timeline
- **Announcement**:
- **Deprecation**:
- **Removal**:
## Unresolved Questions
- Question 1
- Question 2
## Future Possibilities
What future work does this enable?
## Impact Assessment
### Performance
Expected performance impact.
### Security
Security considerations and implications.
### Compatibility
- **Backward Compatible**: [Yes / No]
- **Breaking Changes**: [List]
### Maintenance
Long-term maintenance considerations.
## Community Input
### Stakeholders
- [ ] Core team
- [ ] Module developers
- [ ] End users
- [ ] Enterprise customers
### Feedback Period
**Duration**: [e.g., 2 weeks]
**Deadline**: [date]
## Implementation Timeline
### Phase 1: Design
- [ ] RFC discussion
- [ ] Design finalization
- [ ] Approval
### Phase 2: Implementation
- [ ] Core implementation
- [ ] Tests
- [ ] Documentation
### Phase 3: Release
- [ ] Beta release
- [ ] Feedback collection
- [ ] Stable release
## Success Metrics
How will we measure success?
- Metric 1
- Metric 2
## References
- Related RFCs:
- External documentation:
- Prior art:
## Open Questions for Community
1. Question 1?
2. Question 2?
---
**Note**: This RFC is open for community discussion. Please provide feedback in the comments below.

66
.github/ISSUE_TEMPLATE/security.md vendored Normal file
View File

@@ -0,0 +1,66 @@
---
name: Security Vulnerability Report
about: Report a security vulnerability (use only for non-critical issues)
title: '[SECURITY] '
labels: 'security'
assignees: ''
---
<!--
SPDX-License-Identifier: GPL-3.0-or-later
Copyright (C) 2024-2026 Moko Consulting Tech
File: .github/ISSUE_TEMPLATE/security.md
Description: Issue template for security vulnerability reports
Project: .github-private
Author: Moko Consulting Tech
Version: 03.02.00
Revision History:
- 2026-01-04: Added MokoStandards compliant header with copyright, file info, and metadata
- 2026-03-11: Version bump to 03.02.00 to match MokoStandards
- 2024: Initial creation
-->
## ⚠️ IMPORTANT: Private Disclosure Required
**For critical security vulnerabilities, DO NOT use this template.**
Follow the process in [SECURITY.md](../SECURITY.md) for responsible disclosure.
Use this template only for:
- Security improvements
- Non-critical security suggestions
- Security documentation updates
---
## Security Issue
**Severity**:
<!-- Low, Medium, or informational only -->
## Description
<!-- Describe the security concern or improvement suggestion -->
## Affected Components
<!-- List the affected files, features, or components -->
## Suggested Mitigation
<!-- Describe how this could be addressed -->
## Standards Reference
Does this relate to security standards in [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards)?
- [ ] SPDX license identifiers
- [ ] Secret management
- [ ] Dependency security
- [ ] Access control
- [ ] Other: [specify]
## Additional Context
<!-- Add any other context about the security concern -->
## Checklist
- [ ] This is NOT a critical vulnerability requiring private disclosure
- [ ] I have reviewed the SECURITY.md policy
- [ ] I have provided sufficient detail for evaluation

View File

@@ -1,346 +1,285 @@
# GitHub Copilot Instructions for MokoCassiopeia
> [!IMPORTANT]
> **🔧 AI Self-Update Required on First Use**
>
> This file may contain unfilled `{{TOKEN}}` placeholders. The MokoStandards bulk-sync system
> replaces them automatically at sync time; if you can still see them, sync has not yet run for
> this repository — or this is a brand-new repo.
>
> **On your very first interaction in this repository, before answering any other question:**
>
> 1. Check whether any `{{TOKEN}}` placeholders remain in this file.
> 2. If they do, locate the values as described in the table below and replace every occurrence
> in **both** `.github/copilot-instructions.md` and `.github/CLAUDE.md`.
> 3. After replacing all tokens, **delete this entire notice block** (from `> [!IMPORTANT]`
> through the closing `> ---` line) in both files.
> 4. Tell the developer: *"I've self-updated the AI context files with this repository's
> values — all set."*
>
> | Placeholder | Where to find the value |
> |---|---|
> | `MokoCassiopeia` | The GitHub repository name (visible in the URL, `README.md` heading, or `git remote -v`) |
> | `https://github.com/mokoconsulting-tech/MokoCassiopeia` | Full GitHub URL, e.g. `https://github.com/mokoconsulting-tech/<repo-name>` |
> | `{{EXTENSION_NAME}}` | The `<name>` element in `manifest.xml` at the repository root |
> | `{{EXTENSION_TYPE}}` | The `type` attribute of the `<extension>` tag in `manifest.xml` (`component`, `module`, `plugin`, or `template`) |
> | `{{EXTENSION_ELEMENT}}` | The `<element>` tag in `manifest.xml`, or the filename prefix (e.g. `com_myextension`, `mod_mymodule`) |
>
> ---
## Repository Context
# MokoCassiopeia — GitHub Copilot Custom Instructions
MokoCassiopeia is a modern, lightweight Joomla template that extends Cassiopeia (Joomla's default template). It follows a strict **non-replacement philosophy** for maximum upgrade compatibility.
## What This Repo Is
**Key Characteristics:**
- Joomla 4.4.x and 5.x compatible
- PHP 8.0+ required
- Built on Cassiopeia template
- Minimal core template overrides
- Alternative layout naming convention (not default replacements)
- GPL-3.0-or-later license
This is a **Moko Consulting MokoWaaS** (Joomla) repository governed by [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards). All coding standards, workflows, and policies are defined there and enforced here via bulk sync.
## Project Structure
Repository URL: https://github.com/mokoconsulting-tech/MokoCassiopeia
Extension name: **{{EXTENSION_NAME}}**
Extension type: **{{EXTENSION_TYPE}}** (`{{EXTENSION_ELEMENT}}`)
Platform: **Joomla 4.x / MokoWaaS**
```
MokoCassiopeia/
├── .github/ # GitHub workflows and configuration
├── docs/ # Comprehensive documentation
├── scripts/ # Build and deployment scripts
├── src/ # Template source files (main working directory)
│ ├── index.php # Main template file
│ ├── templateDetails.xml # Joomla template manifest
│ ├── joomla.asset.json # Web Asset Manager configuration
│ ├── language/ # Frontend language files (en-GB, en-US)
│ ├── html/ # Alternative layout overrides
│ └── media/ # CSS, JS, images, fonts, vendors
├── templates/ # Custom color scheme templates
└── tests/ # Codeception tests
```
---
## Critical Conventions
## Primary Language
### 1. Override Philosophy: Alternative Layouts Only
**PHP** (≥ 7.4) is the primary language for this Joomla extension. JavaScript may be used for frontend enhancements. YAML uses 2-space indentation. All other text files use tabs per `.editorconfig`.
**NEVER replace default layouts.** All overrides must use alternative layout names.
---
**Correct:**
- `mobile.php` (alternative layout)
- `mainmenu.php` (alternative layout for mod_menu)
- `toc-left.php`, `toc-right.php` (alternative layouts with TOC)
## File Header — Always Required on New Files
**Incorrect:**
- `default.php` (replaces core layout - FORBIDDEN)
- Any file that replaces Cassiopeia's default behavior
**Rationale:** Ensures Joomla core updates don't break the template and gives users choice.
### 2. File Headers
All source files must include standardized copyright headers:
Every new file needs a copyright header as its first content.
**PHP:**
```php
<?php
/**
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
*
* This file is part of a Moko Consulting project.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* @defgroup Joomla.Template.Site
* @ingroup MokoCassiopeia.Template
* @brief [Brief description]
* @version [Version number]
* FILE INFORMATION
* DEFGROUP: MokoCassiopeia.{{EXTENSION_TYPE}}
* INGROUP: MokoCassiopeia
* REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
* PATH: /path/to/file.php
* VERSION: XX.YY.ZZ
* BRIEF: One-line description of purpose
*/
defined('_JEXEC') or die;
```
### 3. Language Files
**Key Rules:**
- Template metadata (name, description) goes in `.sys.ini` files ONLY
- Frontend runtime strings go in `.ini` files
- Language files location: `src/language/{locale}/`
- NO `folder` attribute in `<languages>` section of templateDetails.xml
- Paths must be relative from package root (e.g., `language/en-GB/tpl_mokocassiopeia.sys.ini`)
**Example templateDetails.xml:**
```xml
<languages folder="language">
<language tag="en-GB">en-GB/tpl_mokocassiopeia.ini</language>
<language tag="en-GB">en-GB/tpl_mokocassiopeia.sys.ini</language>
</languages>
```
### 4. Hardcoded XML Description
The template description in `templateDetails.xml` is **hardcoded using CDATA**, not a language constant:
```xml
<description><![CDATA[Modern, lightweight Joomla template...]]></description>
```
**Rationale:** Ensures immediate availability during installation without language file dependency.
### 5. Version Format
Follow semantic versioning: `XX.YY.ZZ`
- XX: Major version
- YY: Minor version
- ZZ: Patch version
**Examples:** `03.06.03`, `03.08.04`
## Coding Standards
### PHP
- **PSR-12 compliant** where not conflicting with Joomla standards
- **Joomla Coding Standards** for Joomla-specific code
- Use `defined('_JEXEC') or die;` at the top of every PHP file
- Type hints for PHP 8.0+ features
- Strict types declarations where appropriate
### JavaScript
- **ES6+** syntax
- Use `'use strict';`
- Prefer `const` and `let` over `var`
- Document functions with JSDoc comments
- Integrate with Joomla's Web Asset Manager
### CSS
- **CSS Variables** for theming (see `docs/CSS_VARIABLES.md`)
- Light/dark mode support via `data-bs-theme` attribute
- Bootstrap 5 utility classes
- Mobile-first responsive design
- Namespace custom classes to avoid conflicts
### Asset Management
All assets must be registered in `joomla.asset.json`:
```json
{
"name": "template.mokocassiopeia.custom",
"version": "1.0.0",
"description": "Custom asset",
"license": "GPL-3.0-or-later",
"dependencies": ["core"],
"js": ["js/custom.js"],
"css": ["css/custom.css"]
}
```
## Development Workflow
### Branching Strategy
- **Development:** Work on feature branches
- **Branch naming:** `feature/description`, `fix/issue-description`
- **Protected branches:** Cannot commit directly to `main` or version branches
- See `docs/WORKFLOW_GUIDE.md` for complete workflow
### Commit Messages
```
Brief summary (50 chars or less)
Detailed explanation if needed:
- What changed
- Why it changed
- Impact of the change
```
**Example:**
```
Fix language file installation paths in templateDetails.xml
- Remove folder attribute from <languages> section
- Update paths to be relative from package root
- Ensures proper installation to JOOMLA_ROOT/language/
```
### Quality Checks
Before committing:
1. **PHP CodeSniffer:** `phpcs --standard=Joomla src/`
2. **PHPStan:** Static analysis for PHP code
3. **Pre-commit hooks:** Automatically run validation
## Testing
### Manual Testing
- Test in Joomla 4.4.x and 5.x
- Test on PHP 8.0, 8.1, 8.2
- Test light/dark mode switching
- Test responsive layouts (mobile/tablet/desktop)
- Test alternative layouts activation
### Test Environment
- Use local Joomla installation
- See `docs/JOOMLA_DEVELOPMENT.md` for setup
## Documentation Standards
### Markdown Files
All documentation must include:
1. **Copyright header**
**Markdown:**
```markdown
<!--
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
This file is part of a Moko Consulting project.
SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
DEFGROUP: [Group]
INGROUP: [Subgroup]
DEFGROUP: MokoCassiopeia.Documentation
INGROUP: MokoCassiopeia
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
FILE: [filename]
VERSION: [version]
BRIEF: [Brief description]
PATH: /docs/file.md
VERSION: XX.YY.ZZ
BRIEF: One-line description
-->
```
2. **Metadata section** (at end)
3. **Revision history table** (at end)
### Code Comments
- Use DocBlocks for functions and classes
- Explain "why" not just "what"
- Keep comments up-to-date with code changes
## Common Patterns
### Theme System Integration
Use CSS variables for colors:
```css
:root[data-bs-theme="light"] {
--color-primary: #1e40af;
--body-bg: #ffffff;
}
:root[data-bs-theme="dark"] {
--color-primary: #60a5fa;
--body-bg: #1f2937;
}
```
### Alternative Layout Structure
```php
<?php
/**
* Alternative layout: mobile
* Activated via: Advanced → Alternative Layout → "mobile"
*/
defined('_JEXEC') or die;
use Joomla\CMS\Layout\LayoutHelper;
// Layout implementation
?>
```
### JavaScript Asset Loading
```javascript
// Register in joomla.asset.json, then use:
Joomla.getOptions('template.mokocassiopeia.options');
```
## Build and Release
### Local Build
```bash
cd scripts
./build-release.sh
```
### Automated Release
- Tags trigger GitHub Actions workflow
- Format: `XX.YY.ZZ`
- Generates ZIP packages with checksums
- Updates `updates.xml` automatically
## Important Don'ts
**Never:**
- Replace default Joomla layouts (use alternative layouts)
- Commit without running code quality checks
- Add version numbers to revision history (use VERSION metadata)
- Hardcode absolute paths (use Joomla path constants)
- Remove copyright headers
- Modify `vendor/` directory contents
- Change language constants location (.sys.ini vs .ini)
- Add `folder` attribute to `<languages>` in templateDetails.xml
## Important Do's
**Always:**
- Use alternative layout names (mobile.php, mainmenu.php, etc.)
- Include copyright headers in all files
- Test in both Joomla 4.x and 5.x
- Test light/dark mode themes
- Update documentation when changing functionality
- Follow semantic versioning
- Register assets in joomla.asset.json
- Use CSS variables for theming
- Keep minimal overrides philosophy
- Document activation steps for alternative layouts
## Key Documentation References
- **Quick Start:** `docs/QUICK_START.md` - First-time setup
- **Override Philosophy:** `docs/OVERRIDE_PHILOSOPHY.md` - Critical reading
- **Development Guide:** `docs/JOOMLA_DEVELOPMENT.md` - Complete dev workflow
- **CSS Variables:** `docs/CSS_VARIABLES.md` - Theming reference
- **Workflow Guide:** `docs/WORKFLOW_GUIDE.md` - Git workflow
## Security Considerations
- Follow `SECURITY.md` for security issues
- Never commit credentials or API keys
- Validate and sanitize all user inputs
- Use Joomla's security features (CSRF tokens, etc.)
- Keep dependencies updated
## License Compliance
All code contributions must be:
- GPL-3.0-or-later compatible
- Properly attributed (third-party code)
- Include SPDX license identifiers
- Maintain existing copyright notices
## Contact and Support
- **Issues:** GitHub Issues
- **Discussions:** GitHub Discussions
- **Security:** Follow SECURITY.md procedures
- **Maintainer:** Moko Consulting Engineering
**YAML / Shell / XML:** Use the appropriate comment syntax with the same fields. JSON files are exempt.
---
**Last Updated:** 2026-03-04
**Repository:** https://github.com/mokoconsulting-tech/MokoCassiopeia
**Version:** 03.08.04
## Version Management
**`README.md` is the single source of truth for the repository version.**
- **Bump the patch version on every PR** — increment `XX.YY.ZZ` (e.g. `01.02.03``01.02.04`) in `README.md` before opening the PR; the `sync-version-on-merge` workflow propagates it automatically to all badges and `FILE INFORMATION` headers on merge to `main`.
- The `VERSION: XX.YY.ZZ` field in `README.md` governs all other version references.
- Version format is zero-padded semver: `XX.YY.ZZ` (e.g. `01.02.03`).
- Never hardcode a specific version in document body text — use the badge or FILE INFORMATION header only.
### Joomla Version Alignment
The version in `README.md` **must always match** the `<version>` tag in `manifest.xml` and the latest entry in `update.xml`. The `make release` command / release workflow updates all three automatically.
```xml
<!-- In manifest.xml — must match README.md version -->
<version>01.02.04</version>
<!-- In update.xml — prepend a new <update> block for every release.
Note: the backslash in version="4\.[0-9]+" is a literal backslash character
in the XML attribute value. Joomla's update server treats the value as a
regular expression, so \. matches a literal dot. -->
<updates>
<update>
<name>{{EXTENSION_NAME}}</name>
<version>01.02.04</version>
<downloads>
<downloadurl type="full" format="zip">
https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/01.02.04/{{EXTENSION_ELEMENT}}-01.02.04.zip
</downloadurl>
</downloads>
<targetplatform name="joomla" version="4\.[0-9]+" />
</update>
<!-- … older entries preserved below … -->
</updates>
```
---
## Joomla Extension Structure
```
MokoCassiopeia/
├── manifest.xml # Joomla installer manifest (root — required)
├── update.xml # Update server manifest (root — required, see below)
├── site/ # Frontend (site) code
│ ├── controller.php
│ ├── controllers/
│ ├── models/
│ └── views/
├── admin/ # Backend (admin) code
│ ├── controller.php
│ ├── controllers/
│ ├── models/
│ ├── views/
│ └── sql/
├── language/ # Language INI files
├── media/ # CSS, JS, images (deployed to /media/{{EXTENSION_ELEMENT}}/)
├── docs/ # Technical documentation
├── tests/ # Test suite
├── .github/
│ ├── workflows/
│ ├── copilot-instructions.md # This file
│ └── CLAUDE.md
├── README.md # Version source of truth
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE # GPL-3.0-or-later
└── Makefile # Build automation
```
---
## update.xml — Required in Repo Root
`update.xml` **must exist at the repository root**. It is the Joomla update server manifest that allows Joomla installations to check for new versions of this extension.
The `manifest.xml` must reference it via:
```xml
<updateservers>
<server type="extension" priority="1" name="{{EXTENSION_NAME}}">
https://github.com/mokoconsulting-tech/MokoCassiopeia/raw/main/update.xml
</server>
</updateservers>
```
**Rules:**
- Every release must prepend a new `<update>` block at the top of `update.xml` — old entries must be preserved below.
- The `<version>` in `update.xml` must exactly match `<version>` in `manifest.xml` and the version in `README.md`.
- The `<downloadurl>` must be a publicly accessible direct download link (GitHub Releases asset URL).
- `<targetplatform name="joomla" version="4\.[0-9]+">` — the backslash is a **literal backslash character** in the XML attribute value; Joomla's update-server parser treats the value as a regular expression, so `\.` matches a literal dot and `[0-9]+` matches one or more digits. Do not double-escape it.
---
## manifest.xml Rules
- Lives at the repo root as `manifest.xml` (not inside `site/` or `admin/`).
- `<version>` tag must be kept in sync with `README.md` version and `update.xml`.
- Must include `<updateservers>` block pointing to this repo's `update.xml`.
- Must include `<files folder="site">` and `<administration>` sections.
- Joomla 4.x requires `<namespace path="src">Moko\{{EXTENSION_NAME}}</namespace>` for namespaced extensions.
---
## GitHub Actions — Token Usage
Every workflow must use **`secrets.GH_TOKEN`** (the org-level Personal Access Token).
```yaml
# ✅ Correct
- uses: actions/checkout@v4
with:
token: ${{ secrets.GH_TOKEN }}
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
```
```yaml
# ❌ Wrong — never use these in workflows
token: ${{ github.token }}
token: ${{ secrets.GITHUB_TOKEN }}
```
---
## MokoStandards Reference
This repository is governed by [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards). Authoritative policies:
| Document | Purpose |
|----------|---------|
| [file-header-standards.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/file-header-standards.md) | Copyright-header rules for every file type |
| [coding-style-guide.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/coding-style-guide.md) | Naming and formatting conventions |
| [branching-strategy.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/branching-strategy.md) | Branch naming, hierarchy, and release workflow |
| [merge-strategy.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/merge-strategy.md) | Squash-merge policy and PR title/body conventions |
| [changelog-standards.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/changelog-standards.md) | How and when to update CHANGELOG.md |
| [joomla-development-guide.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/guide/waas/joomla-development-guide.md) | MokoWaaS Joomla extension development guide |
---
## Naming Conventions
| Context | Convention | Example |
|---------|-----------|---------|
| PHP class | `PascalCase` | `MyController` |
| PHP method / function | `camelCase` | `getItems()` |
| PHP variable | `$snake_case` | `$item_id` |
| PHP constant | `UPPER_SNAKE_CASE` | `MAX_ITEMS` |
| PHP class file | `PascalCase.php` | `ItemModel.php` |
| YAML workflow | `kebab-case.yml` | `ci-joomla.yml` |
| Markdown doc | `kebab-case.md` | `installation-guide.md` |
---
## Commit Messages
Format: `<type>(<scope>): <subject>` — imperative, lower-case subject, no trailing period.
Valid types: `feat` · `fix` · `docs` · `chore` · `ci` · `refactor` · `style` · `test` · `perf` · `revert` · `build`
---
## Branch Naming
Format: `<prefix>/<MAJOR.MINOR.PATCH>[/description]`
Approved prefixes: `dev/` · `rc/` · `version/` · `patch/` · `copilot/` · `dependabot/`
---
## Keeping Documentation Current
| Change type | Documentation to update |
|-------------|------------------------|
| New or renamed PHP class/method | PHPDoc block; `docs/api/` entry |
| New or changed manifest.xml | Update `update.xml` version; bump README.md version |
| New release | Prepend `<update>` block to `update.xml`; update CHANGELOG.md; bump README.md version |
| New or changed workflow | `docs/workflows/<workflow-name>.md` |
| Any modified file | Update the `VERSION` field in that file's `FILE INFORMATION` block |
| **Every PR** | **Bump the patch version** — increment `XX.YY.ZZ` in `README.md`; `sync-version-on-merge` propagates it |
---
## Key Constraints
- Never commit directly to `main` — all changes go via PR, squash-merged
- Never skip the FILE INFORMATION block on a new file
- Never add `defined('_JEXEC') or die;` to CLI scripts or model tests — only to web-accessible PHP files
- Never hardcode version numbers in body text — update `README.md` and let automation propagate
- Never use `github.token` or `secrets.GITHUB_TOKEN` in workflows — always use `secrets.GH_TOKEN`
- Never let `manifest.xml` version, `update.xml` version, and `README.md` version go out of sync

55
.github/copilot.yml vendored
View File

@@ -37,11 +37,66 @@ allowed_domains:
# PHP and Joomla specific
- "joomla.org" # Joomla CMS
- "docs.joomla.org" # Joomla documentation
- "downloads.joomla.org" # Joomla core downloads
- "php.net" # PHP documentation
- "getcomposer.org" # Composer dependency manager
- "packagist.org" # Composer package registry (also listed under packages)
# Dolibarr specific
- "dolibarr.org" # Dolibarr ERP/CRM
- "wiki.dolibarr.org" # Dolibarr wiki
- "docs.dolibarr.org" # Dolibarr developer documentation
# Moko Consulting
- "mokoconsulting.tech" # Moko Consulting main site
- "*.mokoconsulting.tech" # All Moko Consulting subdomains (API, docs, CDN, etc.)
# Google services
- "drive.google.com" # Google Drive (file sharing and assets)
- "docs.google.com" # Google Docs
- "sheets.google.com" # Google Sheets
- "accounts.google.com" # Google authentication
- "storage.googleapis.com" # Google Cloud Storage
- "*.googleapis.com" # Google APIs (Maps, Fonts, etc.)
- "*.googleusercontent.com" # Google user-uploaded content and CDN
- "fonts.googleapis.com" # Google Fonts CSS
- "fonts.gstatic.com" # Google Fonts static assets
# GitHub extended
- "api.github.com" # GitHub REST API
- "upload.github.com" # GitHub file uploads
- "objects.githubusercontent.com" # GitHub release assets and LFS
- "user-images.githubusercontent.com" # GitHub issue/PR image attachments
- "codeload.github.com" # GitHub archive downloads
- "ghcr.io" # GitHub Container Registry
- "pkg.github.com" # GitHub Packages
# Developer reference
- "developer.mozilla.org" # MDN Web Docs
- "stackoverflow.com" # Stack Overflow
- "git-scm.com" # Git documentation
# CDN and infrastructure
- "cdn.jsdelivr.net" # jsDelivr CDN
- "unpkg.com" # unpkg CDN
- "cdnjs.cloudflare.com" # Cloudflare CDN
- "img.shields.io" # Shields.io badge images
- "shields.io" # Shields.io badge service
# Container registries
- "hub.docker.com" # Docker Hub
- "registry-1.docker.io" # Docker registry pulls
- "index.docker.io" # Docker index
# CI / code quality
- "codecov.io" # Code coverage reporting
- "coveralls.io" # Coveralls coverage service
- "sonarcloud.io" # SonarCloud static analysis
# Terraform / infrastructure
- "registry.terraform.io" # Terraform provider registry
- "releases.hashicorp.com" # HashiCorp release downloads
- "checkpoint-api.hashicorp.com" # HashiCorp update checks
# Settings for code generation and suggestions
copilot:

115
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@@ -0,0 +1,115 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# This file is part of a Moko Consulting project.
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: GitHub.Workflow.Template
# INGROUP: MokoStandards.Security
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
# PATH: /templates/workflows/generic/codeql-analysis.yml
# VERSION: 04.00.15
# BRIEF: CodeQL security scanning workflow (generic — all repo types)
# NOTE: Deployed to .github/workflows/codeql-analysis.yml in governed repos.
# CodeQL does not support PHP directly; JavaScript scans JSON/YAML/shell.
# For PHP-specific security scanning see standards-compliance.yml.
name: CodeQL Security Scanning
on:
push:
branches:
- main
- dev/**
- rc/**
- version/**
pull_request:
branches:
- main
- dev/**
- rc/**
schedule:
# Weekly on Monday at 06:00 UTC
- cron: '0 6 * * 1'
workflow_dispatch:
permissions:
actions: read
contents: read
security-events: write
pull-requests: read
jobs:
analyze:
name: Analyze (${{ matrix.language }})
runs-on: ubuntu-latest
timeout-minutes: 360
strategy:
fail-fast: false
matrix:
# CodeQL does not support PHP. Use 'javascript' to scan JSON, YAML,
# and shell scripts. Add 'actions' to scan GitHub Actions workflows.
language: ['javascript', 'actions']
steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
queries: security-extended,security-and-quality
- name: Autobuild
uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{ matrix.language }}"
upload: true
output: sarif-results
wait-for-processing: true
- name: Upload SARIF results
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.5.0
with:
name: codeql-results-${{ matrix.language }}
path: sarif-results
retention-days: 30
- name: Step summary
if: always()
run: |
echo "### 🔍 CodeQL — ${{ matrix.language }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
URL="https://github.com/${{ github.repository }}/security/code-scanning"
echo "See the [Security tab]($URL) for findings." >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Severity | SLA |" >> $GITHUB_STEP_SUMMARY
echo "|----------|-----|" >> $GITHUB_STEP_SUMMARY
echo "| Critical | 7 days |" >> $GITHUB_STEP_SUMMARY
echo "| High | 14 days |" >> $GITHUB_STEP_SUMMARY
echo "| Medium | 30 days |" >> $GITHUB_STEP_SUMMARY
echo "| Low | 60 days / next release |" >> $GITHUB_STEP_SUMMARY
summary:
name: Security Scan Summary
runs-on: ubuntu-latest
needs: analyze
if: always()
steps:
- name: Summary
run: |
echo "### 🛡️ CodeQL Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Trigger:** ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY
echo "**Branch:** ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
SECURITY_URL="https://github.com/${{ github.repository }}/security"
echo "" >> $GITHUB_STEP_SUMMARY
echo "📊 [View all security alerts]($SECURITY_URL)" >> $GITHUB_STEP_SUMMARY

587
.github/workflows/deploy-dev.yml vendored Normal file
View File

@@ -0,0 +1,587 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# This file is part of a Moko Consulting project.
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# FILE INFORMATION
# DEFGROUP: GitHub.Workflow
# INGROUP: MokoStandards.Deploy
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
# PATH: /templates/workflows/shared/deploy-dev.yml
# VERSION: 04.00.25
# BRIEF: SFTP deployment workflow for development server — synced to all governed repos
# NOTE: Synced via bulk-repo-sync to .github/workflows/deploy-dev.yml in all governed repos.
# Port is resolved in order: DEV_FTP_PORT variable → :port suffix in DEV_FTP_HOST → 22.
name: Deploy to Dev Server (SFTP)
# Deploys the contents of the src/ directory to the development server via SFTP.
# Triggers on every pull_request to development branches (so the dev server always
# reflects the latest PR state) and on push/merge to main branches.
#
# Required org-level variables: DEV_FTP_HOST, DEV_FTP_PATH, DEV_FTP_USERNAME
# Optional org-level variable: DEV_FTP_PORT (auto-detected from host or defaults to 22)
# Optional org/repo variable: DEV_FTP_PATH_SUFFIX
# Optional org/repo variable: CUSTOM_FOLDER — when set, appended to the remote path after
# DEV_FTP_PATH_SUFFIX; used automatically for Dolibarr modules
# Optional org/repo variable: FTP_IGNORE — comma-delimited list of regex patterns, each enclosed in
# double quotes, for files/paths to exclude from upload, e.g.:
# "\.git*", "\.DS_Store", "configuration\.php", "\.ps1"
# Patterns are tested against the forward-slash relative path of each
# file (e.g. "subdir/file.txt"). The repository .gitignore is also
# respected automatically.
# Required org-level secret: DEV_FTP_KEY (preferred) or DEV_FTP_PASSWORD
#
# Access control: only users with admin or maintain role on the repository may deploy.
on:
push:
branches:
- main
- master
- develop
- dev
- development
paths:
- 'src/**'
pull_request:
types: [opened, synchronize, reopened, closed]
branches:
- main
- master
- develop
- dev
- development
workflow_dispatch:
inputs:
clear_remote:
description: 'Delete all files inside the remote destination folder before uploading'
required: false
default: false
type: boolean
permissions:
contents: read
pull-requests: write
jobs:
check-permission:
name: Verify Deployment Permission
runs-on: ubuntu-latest
steps:
- name: Check actor permission
env:
# Prefer the org-scoped GH_TOKEN secret (needed for the org membership
# fallback). Falls back to the built-in github.token so the collaborator
# endpoint still works even if GH_TOKEN is not configured.
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
run: |
ACTOR="${{ github.actor }}"
REPO="${{ github.repository }}"
ORG="${{ github.repository_owner }}"
# Try the per-repo collaborator endpoint first.
# This returns 404 for org owners who are not listed as explicit
# collaborators, so we fall back to the org membership role check.
PERMISSION=$(gh api "repos/${REPO}/collaborators/${ACTOR}/permission" \
--jq '.permission' 2>/dev/null)
if [ -z "$PERMISSION" ]; then
# Collaborator endpoint returned nothing — try org membership.
# Requires a token with read:org scope (secrets.GH_TOKEN).
# github.token alone is insufficient for this endpoint.
ORG_ROLE=$(gh api "orgs/${ORG}/memberships/${ACTOR}" \
--jq '.role' 2>/dev/null)
if [ "$ORG_ROLE" = "owner" ]; then
PERMISSION="admin"
echo " ${ACTOR} is an org owner — granting admin access"
else
# Both checks failed — token may lack read:org scope.
echo "⚠️ Could not determine permission for ${ACTOR}."
echo " Add GH_TOKEN (PAT with read:org scope) as an org secret to fix this."
PERMISSION="none"
fi
fi
case "$PERMISSION" in
admin|maintain)
echo "✅ ${ACTOR} has '${PERMISSION}' permission — authorized to deploy"
;;
*)
echo "❌ Deployment requires admin or maintain role."
echo " ${ACTOR} has '${PERMISSION}' — contact your org administrator."
exit 1
;;
esac
deploy:
name: SFTP Deploy → Dev
runs-on: ubuntu-latest
needs: [check-permission]
if: >-
github.event_name == 'workflow_dispatch' ||
github.event_name == 'push' ||
(github.event_name == 'pull_request' &&
(github.event.action == 'opened' ||
github.event.action == 'synchronize' ||
github.event.action == 'reopened' ||
github.event.pull_request.merged == true))
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Resolve source directory
id: source
run: |
SRC="src"
if [ ! -d "$SRC" ]; then
echo "⚠️ No src/ directory found — skipping deployment"
echo "skip=true" >> "$GITHUB_OUTPUT"
else
COUNT=$(find "$SRC" -maxdepth 0 -type d > /dev/null && find "$SRC" -type f | wc -l)
echo "✅ Source: src/ (${COUNT} file(s))"
echo "skip=false" >> "$GITHUB_OUTPUT"
echo "dir=${SRC}" >> "$GITHUB_OUTPUT"
fi
- name: Preview files to deploy
if: steps.source.outputs.skip == 'false'
env:
SOURCE_DIR: ${{ steps.source.outputs.dir }}
FTP_IGNORE: ${{ vars.FTP_IGNORE }}
run: |
# ── Parse FTP_IGNORE ─────────────────────────────────────────────────
IGNORE_PATTERNS=()
if [ -n "$FTP_IGNORE" ]; then
while IFS= read -r -d ',' token; do
pattern=$(printf '%s' "$token" | sed 's/^[[:space:]]*"//;s/"[[:space:]]*$//')
[ -n "$pattern" ] && IGNORE_PATTERNS+=("$pattern")
done <<< "${FTP_IGNORE},"
fi
# ── Walk src/ and classify every file ────────────────────────────────
WILL_UPLOAD=()
IGNORED_FILES=()
while IFS= read -r -d '' file; do
rel="${file#${SOURCE_DIR}/}"
SKIP=false
for pat in "${IGNORE_PATTERNS[@]}"; do
if echo "$rel" | grep -qE "$pat" 2>/dev/null; then
IGNORED_FILES+=("$rel | FTP_IGNORE \`$pat\`")
SKIP=true; break
fi
done
$SKIP && continue
if [ -f ".gitignore" ] && git check-ignore -q "$rel" 2>/dev/null; then
IGNORED_FILES+=("$rel | .gitignore")
continue
fi
WILL_UPLOAD+=("$rel")
done < <(find "$SOURCE_DIR" -type f -print0 | sort -z)
UPLOAD_COUNT="${#WILL_UPLOAD[@]}"
IGNORE_COUNT="${#IGNORED_FILES[@]}"
echo " ${UPLOAD_COUNT} file(s) will be uploaded, ${IGNORE_COUNT} ignored"
# ── Write deployment preview to step summary ──────────────────────────
{
echo "## 📋 Deployment Preview"
echo ""
echo "| Field | Value |"
echo "|---|---|"
echo "| Source | \`${SOURCE_DIR}/\` |"
echo "| Files to upload | **${UPLOAD_COUNT}** |"
echo "| Files ignored | **${IGNORE_COUNT}** |"
echo ""
if [ "${UPLOAD_COUNT}" -gt 0 ]; then
echo "### 📂 Files that will be uploaded"
echo '```'
printf '%s\n' "${WILL_UPLOAD[@]}"
echo '```'
echo ""
fi
if [ "${IGNORE_COUNT}" -gt 0 ]; then
echo "### ⏭️ Files excluded"
echo "| File | Reason |"
echo "|---|---|"
for entry in "${IGNORED_FILES[@]}"; do
f="${entry% | *}"; r="${entry##* | }"
echo "| \`${f}\` | ${r} |"
done
echo ""
fi
} >> "$GITHUB_STEP_SUMMARY"
- name: Resolve SFTP host and port
if: steps.source.outputs.skip == 'false'
id: conn
env:
HOST_RAW: ${{ vars.DEV_FTP_HOST }}
PORT_VAR: ${{ vars.DEV_FTP_PORT }}
run: |
HOST="$HOST_RAW"
PORT="$PORT_VAR"
# Priority 1 — explicit DEV_FTP_PORT variable
if [ -n "$PORT" ]; then
echo " Using explicit DEV_FTP_PORT=${PORT}"
# Priority 2 — port embedded in DEV_FTP_HOST (host:port)
elif [[ "$HOST" == *:* ]]; then
PORT="${HOST##*:}"
HOST="${HOST%:*}"
echo " Extracted port ${PORT} from DEV_FTP_HOST"
# Priority 3 — SFTP default
else
PORT="22"
echo " No port specified — defaulting to SFTP port 22"
fi
echo "host=${HOST}" >> "$GITHUB_OUTPUT"
echo "port=${PORT}" >> "$GITHUB_OUTPUT"
echo "SFTP target: ${HOST}:${PORT}"
- name: Build remote path
if: steps.source.outputs.skip == 'false'
id: remote
env:
DEV_FTP_PATH: ${{ vars.DEV_FTP_PATH }}
DEV_FTP_PATH_SUFFIX: ${{ vars.DEV_FTP_PATH_SUFFIX }}
CUSTOM_FOLDER: ${{ vars.CUSTOM_FOLDER }}
run: |
BASE="$DEV_FTP_PATH"
SUFFIX="$DEV_FTP_PATH_SUFFIX"
CUSTOM="$CUSTOM_FOLDER"
if [ -z "$BASE" ]; then
echo "❌ DEV_FTP_PATH is not set."
echo " Configure it as an org-level variable (Settings → Variables) and"
echo " ensure this repository has been granted access to it."
exit 1
fi
# Always append suffix when set — path is BASE/SUFFIX
if [ -n "$SUFFIX" ]; then
REMOTE="${BASE%/}/${SUFFIX#/}"
else
REMOTE="$BASE"
fi
# Append CUSTOM_FOLDER when set — makes Dolibarr module paths automatic
if [ -n "$CUSTOM" ]; then
REMOTE="${REMOTE%/}/${CUSTOM#/}"
echo " CUSTOM_FOLDER appended: ${CUSTOM}"
fi
echo "path=${REMOTE}" >> "$GITHUB_OUTPUT"
echo "Remote path: ${REMOTE}"
- name: Detect SFTP authentication method
if: steps.source.outputs.skip == 'false'
id: auth
env:
HAS_KEY: ${{ secrets.DEV_FTP_KEY }}
HAS_PASSWORD: ${{ secrets.DEV_FTP_PASSWORD }}
run: |
if [ -n "$HAS_KEY" ] && [ -n "$HAS_PASSWORD" ]; then
# Both set: key auth with password as passphrase; falls back to password-only if key fails
echo "method=key" >> "$GITHUB_OUTPUT"
echo "use_passphrase=true" >> "$GITHUB_OUTPUT"
echo "has_password=true" >> "$GITHUB_OUTPUT"
echo " Primary: SSH key + passphrase (DEV_FTP_KEY / DEV_FTP_PASSWORD)"
echo " Fallback: password-only auth if key authentication fails"
elif [ -n "$HAS_KEY" ]; then
# Key only: no passphrase, no password fallback
echo "method=key" >> "$GITHUB_OUTPUT"
echo "use_passphrase=false" >> "$GITHUB_OUTPUT"
echo "has_password=false" >> "$GITHUB_OUTPUT"
echo " Using SSH key authentication (DEV_FTP_KEY, no passphrase, no fallback)"
elif [ -n "$HAS_PASSWORD" ]; then
# Password only: direct SFTP password auth
echo "method=password" >> "$GITHUB_OUTPUT"
echo "use_passphrase=false" >> "$GITHUB_OUTPUT"
echo "has_password=true" >> "$GITHUB_OUTPUT"
echo " Using password authentication (DEV_FTP_PASSWORD)"
else
echo "❌ No SFTP credentials configured."
echo " Set DEV_FTP_KEY (preferred) or DEV_FTP_PASSWORD as an org-level secret."
exit 1
fi
- name: Setup PHP
if: steps.source.outputs.skip == 'false'
uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.31.0
with:
php-version: '8.1'
tools: composer
- name: Setup MokoStandards deploy tools
if: steps.source.outputs.skip == 'false'
env:
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
run: |
git clone --depth 1 --quiet \
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
/tmp/mokostandards
cd /tmp/mokostandards
composer install --no-dev --no-interaction --quiet
- name: Clear remote destination folder
if: >-
steps.source.outputs.skip == 'false' &&
inputs.clear_remote == true
env:
SFTP_HOST: ${{ steps.conn.outputs.host }}
SFTP_PORT: ${{ steps.conn.outputs.port }}
SFTP_USER: ${{ vars.DEV_FTP_USERNAME }}
SFTP_KEY: ${{ secrets.DEV_FTP_KEY }}
SFTP_PASSWORD: ${{ secrets.DEV_FTP_PASSWORD }}
AUTH_METHOD: ${{ steps.auth.outputs.method }}
USE_PASSPHRASE: ${{ steps.auth.outputs.use_passphrase }}
HAS_PASSWORD: ${{ steps.auth.outputs.has_password }}
REMOTE_PATH: ${{ steps.remote.outputs.path }}
run: |
cat > /tmp/moko_clear.php << 'PHPEOF'
<?php
declare(strict_types=1);
require '/tmp/mokostandards/vendor/autoload.php';
use phpseclib3\Net\SFTP;
use phpseclib3\Crypt\PublicKeyLoader;
$host = (string) getenv('SFTP_HOST');
$port = (int) getenv('SFTP_PORT');
$username = (string) getenv('SFTP_USER');
$authMethod = (string) getenv('AUTH_METHOD');
$usePassphrase = getenv('USE_PASSPHRASE') === 'true';
$hasPassword = getenv('HAS_PASSWORD') === 'true';
$remotePath = rtrim((string) getenv('REMOTE_PATH'), '/');
echo "⚠️ Clearing remote folder: {$remotePath}\n";
$sftp = new SFTP($host, $port);
// ── Authentication ──────────────────────────────────────────────
if ($authMethod === 'key') {
$keyData = (string) getenv('SFTP_KEY');
$passphrase = $usePassphrase ? (string) getenv('SFTP_PASSWORD') : false;
$password = $hasPassword ? (string) getenv('SFTP_PASSWORD') : '';
$key = PublicKeyLoader::load($keyData, $passphrase);
if (!$sftp->login($username, $key)) {
if ($password !== '') {
echo "⚠️ Key auth failed — falling back to password\n";
if (!$sftp->login($username, $password)) {
fwrite(STDERR, "❌ Both key and password authentication failed\n");
exit(1);
}
echo "✅ Connected via password authentication (key fallback)\n";
} else {
fwrite(STDERR, "❌ Key authentication failed and no password fallback is available\n");
exit(1);
}
} else {
echo "✅ Connected via SSH key authentication\n";
}
} else {
if (!$sftp->login($username, (string) getenv('SFTP_PASSWORD'))) {
fwrite(STDERR, "❌ Password authentication failed\n");
exit(1);
}
echo "✅ Connected via password authentication\n";
}
// ── Recursive delete ────────────────────────────────────────────
function rmrf(SFTP $sftp, string $path): void
{
$entries = $sftp->nlist($path);
if ($entries === false) {
return; // path does not exist — nothing to clear
}
foreach ($entries as $name) {
if ($name === '.' || $name === '..') {
continue;
}
$entry = "{$path}/{$name}";
if ($sftp->is_dir($entry)) {
rmrf($sftp, $entry);
$sftp->rmdir($entry);
echo " 🗑️ Removed dir: {$entry}\n";
} else {
$sftp->delete($entry);
echo " 🗑️ Removed file: {$entry}\n";
}
}
}
// ── Create remote directory tree ────────────────────────────────
function sftpMakedirs(SFTP $sftp, string $path): void
{
$parts = array_values(array_filter(explode('/', $path), fn(string $p) => $p !== ''));
$current = str_starts_with($path, '/') ? '' : '';
foreach ($parts as $part) {
$current .= '/' . $part;
$sftp->mkdir($current); // silently returns false if already exists
}
}
rmrf($sftp, $remotePath);
sftpMakedirs($sftp, $remotePath);
echo "✅ Remote folder ready: {$remotePath}\n";
PHPEOF
php /tmp/moko_clear.php
- name: Deploy via SFTP
if: steps.source.outputs.skip == 'false'
env:
SFTP_HOST: ${{ steps.conn.outputs.host }}
SFTP_PORT: ${{ steps.conn.outputs.port }}
SFTP_USER: ${{ vars.DEV_FTP_USERNAME }}
SFTP_KEY: ${{ secrets.DEV_FTP_KEY }}
SFTP_PASSWORD: ${{ secrets.DEV_FTP_PASSWORD }}
AUTH_METHOD: ${{ steps.auth.outputs.method }}
USE_PASSPHRASE: ${{ steps.auth.outputs.use_passphrase }}
REMOTE_PATH: ${{ steps.remote.outputs.path }}
SOURCE_DIR: ${{ steps.source.outputs.dir }}
run: |
# ── Write SSH key to temp file (key auth only) ────────────────────────
if [ "$AUTH_METHOD" = "key" ]; then
printf '%s' "$SFTP_KEY" > /tmp/deploy_key
chmod 600 /tmp/deploy_key
fi
# ── Generate sftp-config.json safely via jq ───────────────────────────
if [ "$AUTH_METHOD" = "key" ]; then
jq -n \
--arg host "$SFTP_HOST" \
--argjson port "${SFTP_PORT:-22}" \
--arg user "$SFTP_USER" \
--arg path "$REMOTE_PATH" \
--arg key "/tmp/deploy_key" \
'{host:$host, port:$port, user:$user, remote_path:$path, ssh_key_file:$key}' \
> /tmp/sftp-config.json
else
jq -n \
--arg host "$SFTP_HOST" \
--argjson port "${SFTP_PORT:-22}" \
--arg user "$SFTP_USER" \
--arg path "$REMOTE_PATH" \
--arg pass "$SFTP_PASSWORD" \
'{host:$host, port:$port, user:$user, remote_path:$path, password:$pass}' \
> /tmp/sftp-config.json
fi
# ── Run deploy-sftp.php from MokoStandards ────────────────────────────
DEPLOY_ARGS=(--path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json)
if [ "$USE_PASSPHRASE" = "true" ]; then
DEPLOY_ARGS+=(--key-passphrase "$SFTP_PASSWORD")
fi
php /tmp/mokostandards/api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}"
# (deploy-sftp.php handles dotfile skipping and .ftp_ignore natively)
# Remove temp files that should never be left behind
rm -f /tmp/deploy_key /tmp/sftp-config.json
- name: Create or update failure issue
if: failure()
env:
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
run: |
REPO="${{ github.repository }}"
RUN_URL="${{ github.server_url }}/${REPO}/actions/runs/${{ github.run_id }}"
ACTOR="${{ github.actor }}"
BRANCH="${{ github.ref_name }}"
EVENT="${{ github.event_name }}"
NOW=$(date -u '+%Y-%m-%d %H:%M:%S UTC')
LABEL="deploy-failure"
TITLE="fix: Dev deployment failed — ${REPO}"
BODY="## Dev Deployment Failed
A deployment to the dev server failed and requires attention.
| Field | Value |
|-------|-------|
| **Repository** | \`${REPO}\` |
| **Branch** | \`${BRANCH}\` |
| **Trigger** | ${EVENT} |
| **Actor** | @${ACTOR} |
| **Failed at** | ${NOW} |
| **Run** | [View workflow run](${RUN_URL}) |
### Next steps
1. Review the [workflow run log](${RUN_URL}) for the specific error.
2. Fix the underlying issue (credentials, SFTP connectivity, permissions).
3. Re-trigger the deployment via **Actions → Deploy to Dev Server → Run workflow**.
---
*Auto-created by deploy-dev.yml — close this issue once the deployment is resolved.*"
# Ensure the label exists (idempotent — no-op if already present)
gh label create "$LABEL" \
--repo "$REPO" \
--color "CC0000" \
--description "Automated deploy failure tracking" \
--force 2>/dev/null || true
# Look for an existing open deploy-failure issue
EXISTING=$(gh api "repos/${REPO}/issues?labels=${LABEL}&state=open&per_page=1" \
--jq '.[0].number' 2>/dev/null)
if [ -n "$EXISTING" ] && [ "$EXISTING" != "null" ]; then
gh api "repos/${REPO}/issues/${EXISTING}" \
-X PATCH \
-f title="$TITLE" \
-f body="$BODY" \
--silent
echo "📋 Failure issue #${EXISTING} updated: ${REPO}" >> "$GITHUB_STEP_SUMMARY"
else
gh issue create \
--repo "$REPO" \
--title "$TITLE" \
--body "$BODY" \
--label "$LABEL" \
| tee -a "$GITHUB_STEP_SUMMARY"
fi
- name: Deployment summary
if: always()
run: |
if [ "${{ steps.source.outputs.skip }}" == "true" ]; then
echo "### ⏭️ Deployment Skipped" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "No \`src/\` directory found in this repository." >> "$GITHUB_STEP_SUMMARY"
elif [ "${{ job.status }}" == "success" ]; then
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "### ✅ Dev Deployment Successful" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "| Field | Value |" >> "$GITHUB_STEP_SUMMARY"
echo "|-------|-------|" >> "$GITHUB_STEP_SUMMARY"
echo "| Host | \`${{ steps.conn.outputs.host }}:${{ steps.conn.outputs.port }}\` |" >> "$GITHUB_STEP_SUMMARY"
echo "| Remote path | \`${{ steps.remote.outputs.path }}\` |" >> "$GITHUB_STEP_SUMMARY"
echo "| Source | \`src/\` |" >> "$GITHUB_STEP_SUMMARY"
echo "| Trigger | ${{ github.event_name }} |" >> "$GITHUB_STEP_SUMMARY"
echo "| Auth | ${{ steps.auth.outputs.method }} |" >> "$GITHUB_STEP_SUMMARY"
echo "| Clear remote | ${{ inputs.clear_remote || 'false' }} |" >> "$GITHUB_STEP_SUMMARY"
else
echo "### ❌ Dev Deployment Failed" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "Check the job log above for error details." >> "$GITHUB_STEP_SUMMARY"
fi

View File

@@ -0,0 +1,758 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# This file is part of a Moko Consulting project.
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# FILE INFORMATION
# DEFGROUP: GitHub.Workflow
# INGROUP: MokoStandards.Firewall
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
# PATH: /templates/workflows/shared/enterprise-firewall-setup.yml
# VERSION: 01.00.00
# BRIEF: Enterprise firewall configuration — generates outbound allow-rules including SFTP deployment server
# NOTE: Reads DEV_FTP_HOST / DEV_FTP_PORT variables to include SFTP egress rules alongside HTTPS rules.
name: Enterprise Firewall Configuration
# This workflow provides firewall configuration guidance for enterprise-ready sites
# It generates firewall rules for allowing outbound access to trusted domains
# including license providers, documentation sources, package registries,
# and the SFTP deployment server (DEV_FTP_HOST / DEV_FTP_PORT).
#
# Runs automatically when:
# - Coding agent workflows are triggered (pull requests with copilot/ prefix)
# - Manual workflow dispatch for custom configurations
on:
workflow_dispatch:
inputs:
firewall_type:
description: 'Target firewall type'
required: true
type: choice
options:
- 'iptables'
- 'ufw'
- 'firewalld'
- 'aws-security-group'
- 'azure-nsg'
- 'gcp-firewall'
- 'cloudflare'
- 'all'
default: 'all'
output_format:
description: 'Output format'
required: true
type: choice
options:
- 'shell-script'
- 'json'
- 'yaml'
- 'markdown'
- 'all'
default: 'markdown'
# Auto-run when coding agent creates or updates PRs
pull_request:
branches:
- 'copilot/**'
- 'agent/**'
types: [opened, synchronize, reopened]
# Auto-run on push to coding agent branches
push:
branches:
- 'copilot/**'
- 'agent/**'
permissions:
contents: read
actions: read
jobs:
generate-firewall-rules:
name: Generate Firewall Rules
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Apply Firewall Rules to Runner (Auto-run only)
if: github.event_name != 'workflow_dispatch'
env:
DEV_FTP_HOST: ${{ vars.DEV_FTP_HOST }}
DEV_FTP_PORT: ${{ vars.DEV_FTP_PORT }}
run: |
echo "🔥 Applying firewall rules for coding agent environment..."
echo ""
echo "This step ensures the GitHub Actions runner can access trusted domains"
echo "including license providers, package registries, and documentation sources."
echo ""
# Note: GitHub Actions runners are ephemeral and run in controlled environments
# This step documents what domains are being accessed during the workflow
# Actual firewall configuration is managed by GitHub
cat > /tmp/trusted-domains.txt << 'EOF'
# Trusted domains for coding agent environment
# License Providers
www.gnu.org
opensource.org
choosealicense.com
spdx.org
creativecommons.org
apache.org
fsf.org
# Documentation & Standards
semver.org
keepachangelog.com
conventionalcommits.org
# GitHub & Related
github.com
api.github.com
docs.github.com
raw.githubusercontent.com
ghcr.io
# Package Registries
npmjs.com
registry.npmjs.org
pypi.org
files.pythonhosted.org
packagist.org
repo.packagist.org
rubygems.org
# Platform-Specific
joomla.org
downloads.joomla.org
docs.joomla.org
php.net
getcomposer.org
dolibarr.org
wiki.dolibarr.org
docs.dolibarr.org
# Moko Consulting
mokoconsulting.tech
# SFTP Deployment Server (DEV_FTP_HOST)
${DEV_FTP_HOST:-<not configured>}
# Google Services
drive.google.com
docs.google.com
sheets.google.com
accounts.google.com
storage.googleapis.com
fonts.googleapis.com
fonts.gstatic.com
# GitHub Extended
upload.github.com
objects.githubusercontent.com
user-images.githubusercontent.com
codeload.github.com
pkg.github.com
# Developer Reference
developer.mozilla.org
stackoverflow.com
git-scm.com
# CDN & Infrastructure
cdn.jsdelivr.net
unpkg.com
cdnjs.cloudflare.com
img.shields.io
# Container Registries
hub.docker.com
registry-1.docker.io
# CI & Code Quality
codecov.io
sonarcloud.io
# Terraform & Infrastructure
registry.terraform.io
releases.hashicorp.com
checkpoint-api.hashicorp.com
EOF
echo "✓ Trusted domains documented for this runner"
echo "✓ GitHub Actions runners have network access to these domains"
echo ""
# Test connectivity to key domains
echo "Testing connectivity to key domains..."
for domain in "github.com" "www.gnu.org" "npmjs.com" "pypi.org"; do
if curl -s --max-time 3 -o /dev/null -w "%{http_code}" "https://$domain" | grep -q "200\|301\|302"; then
echo " ✓ $domain is accessible"
else
echo " ⚠️ $domain connectivity check failed (may be expected)"
fi
done
# Test SFTP server connectivity (TCP port check)
SFTP_HOST="${DEV_FTP_HOST:-}"
SFTP_PORT="${DEV_FTP_PORT:-22}"
if [ -n "$SFTP_HOST" ]; then
# Strip any embedded :port suffix
SFTP_HOST="${SFTP_HOST%%:*}"
echo ""
echo "Testing SFTP deployment server connectivity..."
if timeout 5 bash -c "echo >/dev/tcp/${SFTP_HOST}/${SFTP_PORT}" 2>/dev/null; then
echo " ✓ SFTP server ${SFTP_HOST}:${SFTP_PORT} is reachable"
else
echo " ⚠️ SFTP server ${SFTP_HOST}:${SFTP_PORT} is not reachable from runner (firewall rule needed)"
fi
else
echo ""
echo " DEV_FTP_HOST not configured — skipping SFTP connectivity check"
fi
- name: Generate Firewall Configuration
id: generate
env:
DEV_FTP_HOST: ${{ vars.DEV_FTP_HOST }}
DEV_FTP_PORT: ${{ vars.DEV_FTP_PORT }}
run: |
cat > generate_firewall_config.py << 'PYTHON_EOF'
#!/usr/bin/env python3
"""
Enterprise Firewall Configuration Generator
Generates firewall rules for enterprise-ready deployments allowing
access to trusted domains including license providers, documentation
sources, package registries, and platform-specific sites.
"""
import json
import os
import yaml
import sys
from typing import List, Dict
# SFTP deployment server from org variables
_sftp_host_raw = os.environ.get("DEV_FTP_HOST", "").strip()
_sftp_port = os.environ.get("DEV_FTP_PORT", "").strip() or "22"
# Strip embedded :port suffix if present
_sftp_host = _sftp_host_raw.split(":")[0] if _sftp_host_raw else ""
if ":" in _sftp_host_raw and not _sftp_port:
_sftp_port = _sftp_host_raw.split(":")[1]
SFTP_HOST = _sftp_host
SFTP_PORT = int(_sftp_port) if _sftp_port.isdigit() else 22
# Trusted domains from .github/copilot.yml
TRUSTED_DOMAINS = {
"license_providers": [
"www.gnu.org",
"opensource.org",
"choosealicense.com",
"spdx.org",
"creativecommons.org",
"apache.org",
"fsf.org",
],
"documentation_standards": [
"semver.org",
"keepachangelog.com",
"conventionalcommits.org",
],
"github_related": [
"github.com",
"api.github.com",
"docs.github.com",
"raw.githubusercontent.com",
"ghcr.io",
],
"package_registries": [
"npmjs.com",
"registry.npmjs.org",
"pypi.org",
"files.pythonhosted.org",
"packagist.org",
"repo.packagist.org",
"rubygems.org",
],
"standards_organizations": [
"json-schema.org",
"w3.org",
"ietf.org",
],
"platform_specific": [
"joomla.org",
"downloads.joomla.org",
"docs.joomla.org",
"php.net",
"getcomposer.org",
"dolibarr.org",
"wiki.dolibarr.org",
"docs.dolibarr.org",
],
"moko_consulting": [
"mokoconsulting.tech",
],
"google_services": [
"drive.google.com",
"docs.google.com",
"sheets.google.com",
"accounts.google.com",
"storage.googleapis.com",
"fonts.googleapis.com",
"fonts.gstatic.com",
],
"github_extended": [
"upload.github.com",
"objects.githubusercontent.com",
"user-images.githubusercontent.com",
"codeload.github.com",
"pkg.github.com",
],
"developer_reference": [
"developer.mozilla.org",
"stackoverflow.com",
"git-scm.com",
],
"cdn_and_infrastructure": [
"cdn.jsdelivr.net",
"unpkg.com",
"cdnjs.cloudflare.com",
"img.shields.io",
],
"container_registries": [
"hub.docker.com",
"registry-1.docker.io",
],
"ci_code_quality": [
"codecov.io",
"sonarcloud.io",
],
"terraform_infrastructure": [
"registry.terraform.io",
"releases.hashicorp.com",
"checkpoint-api.hashicorp.com",
],
}
# Inject SFTP deployment server as a separate category (port 22, not 443)
if SFTP_HOST:
TRUSTED_DOMAINS["sftp_deployment_server"] = [SFTP_HOST]
print(f" SFTP deployment server: {SFTP_HOST}:{SFTP_PORT}")
def generate_sftp_iptables_rules(host: str, port: int) -> str:
"""Generate iptables rules specifically for SFTP egress"""
return (
f"# Allow SFTP to deployment server {host}:{port}\n"
f"iptables -A OUTPUT -p tcp -d $(dig +short {host} | head -1)"
f" --dport {port} -j ACCEPT # SFTP deploy\n"
)
def generate_sftp_ufw_rules(host: str, port: int) -> str:
"""Generate UFW rules for SFTP egress"""
return (
f"# Allow SFTP to deployment server\n"
f"ufw allow out to $(dig +short {host} | head -1)"
f" port {port} proto tcp comment 'SFTP deploy to {host}'\n"
)
def generate_sftp_firewalld_rules(host: str, port: int) -> str:
"""Generate firewalld rules for SFTP egress"""
return (
f"# Allow SFTP to deployment server\n"
f"firewall-cmd --permanent --add-rich-rule='"
f"rule family=ipv4 destination address=$(dig +short {host} | head -1)"
f" port port={port} protocol=tcp accept' # SFTP deploy\n"
)
def generate_iptables_rules(domains: List[str]) -> str:
"""Generate iptables firewall rules"""
rules = ["#!/bin/bash", "", "# Enterprise Firewall Rules - iptables", ""]
rules.append("# Allow outbound HTTPS to trusted domains")
rules.append("")
for domain in domains:
rules.append(f"# Allow {domain}")
rules.append(f"iptables -A OUTPUT -p tcp -d $(dig +short {domain} | head -1) --dport 443 -j ACCEPT")
rules.append("")
rules.append("# Allow DNS lookups")
rules.append("iptables -A OUTPUT -p udp --dport 53 -j ACCEPT")
rules.append("iptables -A OUTPUT -p tcp --dport 53 -j ACCEPT")
return "\n".join(rules)
def generate_ufw_rules(domains: List[str]) -> str:
"""Generate UFW firewall rules"""
rules = ["#!/bin/bash", "", "# Enterprise Firewall Rules - UFW", ""]
rules.append("# Allow outbound HTTPS to trusted domains")
rules.append("")
for domain in domains:
rules.append(f"# Allow {domain}")
rules.append(f"ufw allow out to $(dig +short {domain} | head -1) port 443 proto tcp comment 'Allow {domain}'")
rules.append("")
rules.append("# Allow DNS")
rules.append("ufw allow out 53/udp comment 'Allow DNS UDP'")
rules.append("ufw allow out 53/tcp comment 'Allow DNS TCP'")
return "\n".join(rules)
def generate_firewalld_rules(domains: List[str]) -> str:
"""Generate firewalld rules"""
rules = ["#!/bin/bash", "", "# Enterprise Firewall Rules - firewalld", ""]
rules.append("# Add trusted domains to firewall")
rules.append("")
for domain in domains:
rules.append(f"# Allow {domain}")
rules.append(f"firewall-cmd --permanent --add-rich-rule='rule family=ipv4 destination address=$(dig +short {domain} | head -1) port port=443 protocol=tcp accept'")
rules.append("")
rules.append("# Reload firewall")
rules.append("firewall-cmd --reload")
return "\n".join(rules)
def generate_aws_security_group(domains: List[str]) -> Dict:
"""Generate AWS Security Group rules (JSON format)"""
rules = {
"SecurityGroupRules": {
"Egress": []
}
}
for domain in domains:
rules["SecurityGroupRules"]["Egress"].append({
"Description": f"Allow HTTPS to {domain}",
"IpProtocol": "tcp",
"FromPort": 443,
"ToPort": 443,
"CidrIp": "0.0.0.0/0", # In practice, resolve to specific IPs
"Tags": [{
"Key": "Domain",
"Value": domain
}]
})
# Add DNS
rules["SecurityGroupRules"]["Egress"].append({
"Description": "Allow DNS",
"IpProtocol": "udp",
"FromPort": 53,
"ToPort": 53,
"CidrIp": "0.0.0.0/0"
})
return rules
def generate_markdown_documentation(domains_by_category: Dict[str, List[str]]) -> str:
"""Generate markdown documentation"""
md = ["# Enterprise Firewall Configuration Guide", ""]
md.append("## Overview")
md.append("")
md.append("This document provides firewall configuration guidance for enterprise-ready deployments.")
md.append("It lists trusted domains that should be whitelisted for outbound access to ensure")
md.append("proper functionality of license validation, package management, and documentation access.")
md.append("")
md.append("## Trusted Domains by Category")
md.append("")
all_domains = []
for category, domains in domains_by_category.items():
category_name = category.replace("_", " ").title()
md.append(f"### {category_name}")
md.append("")
md.append("| Domain | Purpose |")
md.append("|--------|---------|")
for domain in domains:
all_domains.append(domain)
purpose = get_domain_purpose(domain)
md.append(f"| `{domain}` | {purpose} |")
md.append("")
md.append("## Implementation Examples")
md.append("")
md.append("### iptables Example")
md.append("")
md.append("```bash")
md.append("# Allow HTTPS to trusted domain")
md.append(f"iptables -A OUTPUT -p tcp -d $(dig +short {all_domains[0]}) --dport 443 -j ACCEPT")
md.append("```")
md.append("")
md.append("### UFW Example")
md.append("")
md.append("```bash")
md.append("# Allow HTTPS to trusted domain")
md.append(f"ufw allow out to {all_domains[0]} port 443 proto tcp")
md.append("```")
md.append("")
md.append("### AWS Security Group Example")
md.append("")
md.append("```json")
md.append("{")
md.append(' "IpPermissions": [{')
md.append(' "IpProtocol": "tcp",')
md.append(' "FromPort": 443,')
md.append(' "ToPort": 443,')
md.append(' "IpRanges": [{"CidrIp": "0.0.0.0/0", "Description": "HTTPS to trusted domains"}]')
md.append(" }]")
md.append("}")
md.append("```")
md.append("")
md.append("## Ports Required")
md.append("")
md.append("| Port | Protocol | Purpose |")
md.append("|------|----------|---------|")
md.append("| 443 | TCP | HTTPS (secure web access) |")
md.append("| 80 | TCP | HTTP (redirects to HTTPS) |")
md.append("| 53 | UDP/TCP | DNS resolution |")
md.append("")
md.append("## Security Considerations")
md.append("")
md.append("1. **DNS Resolution**: Ensure DNS queries are allowed (port 53 UDP/TCP)")
md.append("2. **Certificate Validation**: HTTPS requires ability to reach certificate authorities")
md.append("3. **Dynamic IPs**: Some domains use CDNs with dynamic IPs - consider using FQDNs in rules")
md.append("4. **Regular Updates**: Review and update whitelist as services change")
md.append("5. **Logging**: Enable logging for blocked connections to identify missing rules")
md.append("")
md.append("## Compliance Notes")
md.append("")
md.append("- All listed domains provide read-only access to public information")
md.append("- License providers enable GPL compliance verification")
md.append("- Package registries support dependency security scanning")
md.append("- No authentication credentials are transmitted to these domains")
md.append("")
return "\n".join(md)
def get_domain_purpose(domain: str) -> str:
"""Get human-readable purpose for a domain"""
purposes = {
"www.gnu.org": "GNU licenses and documentation",
"opensource.org": "Open Source Initiative resources",
"choosealicense.com": "GitHub license selection tool",
"spdx.org": "Software Package Data Exchange identifiers",
"creativecommons.org": "Creative Commons licenses",
"apache.org": "Apache Software Foundation licenses",
"fsf.org": "Free Software Foundation resources",
"semver.org": "Semantic versioning specification",
"keepachangelog.com": "Changelog format standards",
"conventionalcommits.org": "Commit message conventions",
"github.com": "GitHub platform access",
"api.github.com": "GitHub API access",
"docs.github.com": "GitHub documentation",
"raw.githubusercontent.com": "GitHub raw content access",
"npmjs.com": "npm package registry",
"pypi.org": "Python Package Index",
"packagist.org": "PHP Composer package registry",
"rubygems.org": "Ruby gems registry",
"joomla.org": "Joomla CMS platform",
"php.net": "PHP documentation and downloads",
"dolibarr.org": "Dolibarr ERP/CRM platform",
}
return purposes.get(domain, "Trusted resource")
def main():
# Use inputs if provided (manual dispatch), otherwise use defaults (auto-run)
firewall_type = "${{ github.event.inputs.firewall_type }}" or "all"
output_format = "${{ github.event.inputs.output_format }}" or "markdown"
print(f"Running in {'manual' if '${{ github.event.inputs.firewall_type }}' else 'automatic'} mode")
print(f"Firewall type: {firewall_type}")
print(f"Output format: {output_format}")
print("")
# Collect all domains
all_domains = []
for domains in TRUSTED_DOMAINS.values():
all_domains.extend(domains)
# Remove duplicates and sort
all_domains = sorted(set(all_domains))
print(f"Generating firewall rules for {len(all_domains)} trusted domains...")
print("")
# Exclude SFTP server from HTTPS rule generation (different port)
https_domains = [d for d in all_domains if d != SFTP_HOST]
# Generate based on firewall type
if firewall_type in ["iptables", "all"]:
rules = generate_iptables_rules(https_domains)
if SFTP_HOST:
rules += "\n# ── SFTP Deployment Server ──────────────────────────────\n"
rules += generate_sftp_iptables_rules(SFTP_HOST, SFTP_PORT)
with open("firewall-rules-iptables.sh", "w") as f:
f.write(rules)
print("✓ Generated iptables rules: firewall-rules-iptables.sh")
if firewall_type in ["ufw", "all"]:
rules = generate_ufw_rules(https_domains)
if SFTP_HOST:
rules += "\n# ── SFTP Deployment Server ──────────────────────────────\n"
rules += generate_sftp_ufw_rules(SFTP_HOST, SFTP_PORT)
with open("firewall-rules-ufw.sh", "w") as f:
f.write(rules)
print("✓ Generated UFW rules: firewall-rules-ufw.sh")
if firewall_type in ["firewalld", "all"]:
rules = generate_firewalld_rules(https_domains)
if SFTP_HOST:
rules += "\n# ── SFTP Deployment Server ──────────────────────────────\n"
rules += generate_sftp_firewalld_rules(SFTP_HOST, SFTP_PORT)
with open("firewall-rules-firewalld.sh", "w") as f:
f.write(rules)
print("✓ Generated firewalld rules: firewall-rules-firewalld.sh")
if firewall_type in ["aws-security-group", "all"]:
rules = generate_aws_security_group(all_domains)
with open("firewall-rules-aws-sg.json", "w") as f:
json.dump(rules, f, indent=2)
print("✓ Generated AWS Security Group rules: firewall-rules-aws-sg.json")
if output_format in ["yaml", "all"]:
with open("trusted-domains.yml", "w") as f:
yaml.dump(TRUSTED_DOMAINS, f, default_flow_style=False)
print("✓ Generated YAML domain list: trusted-domains.yml")
if output_format in ["json", "all"]:
with open("trusted-domains.json", "w") as f:
json.dump(TRUSTED_DOMAINS, f, indent=2)
print("✓ Generated JSON domain list: trusted-domains.json")
if output_format in ["markdown", "all"]:
md = generate_markdown_documentation(TRUSTED_DOMAINS)
with open("FIREWALL_CONFIGURATION.md", "w") as f:
f.write(md)
print("✓ Generated documentation: FIREWALL_CONFIGURATION.md")
print("")
print("Domain Categories:")
for category, domains in TRUSTED_DOMAINS.items():
print(f" - {category}: {len(domains)} domains")
print("")
print("Total unique domains: ", len(all_domains))
if __name__ == "__main__":
main()
PYTHON_EOF
chmod +x generate_firewall_config.py
pip install PyYAML
python3 generate_firewall_config.py
- name: Upload Firewall Configuration Artifacts
uses: actions/upload-artifact@v6
with:
name: firewall-configurations
path: |
firewall-rules-*.sh
firewall-rules-*.json
trusted-domains.*
FIREWALL_CONFIGURATION.md
retention-days: 90
- name: Display Summary
run: |
echo "## Firewall Configuration" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "**Mode**: Manual Execution" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Firewall rules have been generated for enterprise-ready deployments." >> $GITHUB_STEP_SUMMARY
else
echo "**Mode**: Automatic Execution (Coding Agent Active)" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "This workflow ran automatically because a coding agent (GitHub Copilot) is active." >> $GITHUB_STEP_SUMMARY
echo "Firewall configuration has been validated for the coding agent environment." >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Files Generated" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if ls firewall-rules-* trusted-domains.* FIREWALL_CONFIGURATION.md 2>/dev/null; then
ls -lh firewall-rules-* trusted-domains.* FIREWALL_CONFIGURATION.md 2>/dev/null | awk '{print "- " $9 " (" $5 ")"}' >> $GITHUB_STEP_SUMMARY
else
echo "- Documentation generated" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "### Download Artifacts" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Download the generated firewall configurations from the workflow artifacts." >> $GITHUB_STEP_SUMMARY
else
echo "### Trusted Domains Active" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "The coding agent has access to:" >> $GITHUB_STEP_SUMMARY
echo "- License providers (GPL, OSI, SPDX, Apache, etc.)" >> $GITHUB_STEP_SUMMARY
echo "- Package registries (npm, PyPI, Packagist, RubyGems)" >> $GITHUB_STEP_SUMMARY
echo "- Documentation sources (GitHub, Joomla, Dolibarr, PHP)" >> $GITHUB_STEP_SUMMARY
echo "- Standards organizations (W3C, IETF, JSON Schema)" >> $GITHUB_STEP_SUMMARY
fi
# Usage Instructions:
#
# This workflow runs in two modes:
#
# 1. AUTOMATIC MODE (Coding Agent):
# - Triggers when coding agent branches (copilot/**, agent/**) are pushed or PR'd
# - Validates firewall configuration for the coding agent environment
# - Documents accessible domains for compliance
# - Ensures license sources and package registries are available
#
# 2. MANUAL MODE (Enterprise Configuration):
# - Manually trigger from the Actions tab
# - Select desired firewall type and output format
# - Download generated artifacts
# - Apply firewall rules to your enterprise environment
#
# Configuration:
# - Trusted domains are sourced from .github/copilot.yml
# - Modify copilot.yml to add/remove trusted domains
# - Changes automatically propagate to firewall rules
#
# Important Notes:
# - Review generated rules before applying to production
# - Some domains may use CDNs with dynamic IPs
# - Consider using FQDN-based rules where supported
# - Test thoroughly in staging environment first
# - Monitor logs for blocked connections
# - Update rules as domains/services change

2534
.github/workflows/standards-compliance.yml vendored Normal file

File diff suppressed because it is too large Load Diff

111
.gitignore vendored
View File

@@ -138,7 +138,6 @@ package-lock.json
# PHP / Composer tooling
# ============================================================
vendor/
!src/media/vendor/
composer.lock
*.phar
codeception.phar
@@ -187,34 +186,15 @@ venv/
hypothesis/
# ============================================================
# Dolibarr (base + runtime)
# Joomla Development
# ============================================================
documents/
dolibarr_documents/
custom/
uploads/
thumbs/
data/
cache/
temp/
tmp/
logs/
htdocs/documents/
htdocs/custom/
htdocs/cache/
htdocs/tmp/
htdocs/logs/
# Custom user overrides for Joomla development
user.css
user.js
colors_custom.css
# ============================================================
# Template Customizations
# ============================================================
# Ignore custom theme files to prevent fork-specific customizations
src/media/css/theme/light.custom.css
src/media/css/theme/dark.custom.css
# Ignore user customization files to prevent local development overrides
src/media/css/user.css
src/media/js/user.js
# Joomla Module Builder auto-generated files
modulebuilder.txt
# ============================================================
# Joomla Core
@@ -326,7 +306,12 @@ src/media/js/user.js
/administrator/language/en-GB/en-GB.com_wrapper.ini
/administrator/language/en-GB/en-GB.com_wrapper.sys.ini
/administrator/language/en-GB/en-GB.ini
/administrator/language/en-GB/en-GB.lib_fof.ini
/administrator/language/en-GB/en-GB.lib_fof.sys.ini
/administrator/language/en-GB/en-GB.lib_joomla.ini
/administrator/language/en-GB/en-GB.lib_phpass.ini
/administrator/language/en-GB/en-GB.lib_phpmailer.ini
/administrator/language/en-GB/en-GB.lib_simplepie.ini
/administrator/language/en-GB/en-GB.localise.php
/administrator/language/en-GB/en-GB.mod_custom.ini
/administrator/language/en-GB/en-GB.mod_custom.sys.ini
@@ -390,6 +375,8 @@ src/media/js/user.js
/administrator/language/en-GB/en-GB.plg_content_contact.sys.ini
/administrator/language/en-GB/en-GB.plg_content_emailcloak.ini
/administrator/language/en-GB/en-GB.plg_content_emailcloak.sys.ini
/administrator/language/en-GB/en-GB.plg_content_example.ini
/administrator/language/en-GB/en-GB.plg_content_example.sys.ini
/administrator/language/en-GB/en-GB.plg_content_fields.ini
/administrator/language/en-GB/en-GB.plg_content_fields.sys.ini
/administrator/language/en-GB/en-GB.plg_content_finder.ini
@@ -406,6 +393,12 @@ src/media/js/user.js
/administrator/language/en-GB/en-GB.plg_content_pagenavigation.sys.ini
/administrator/language/en-GB/en-GB.plg_content_vote.ini
/administrator/language/en-GB/en-GB.plg_content_vote.sys.ini
/administrator/language/en-GB/en-GB.plg_editors_codemirror.ini
/administrator/language/en-GB/en-GB.plg_editors_codemirror.sys.ini
/administrator/language/en-GB/en-GB.plg_editors_none.ini
/administrator/language/en-GB/en-GB.plg_editors_none.sys.ini
/administrator/language/en-GB/en-GB.plg_editors_tinymce.ini
/administrator/language/en-GB/en-GB.plg_editors_tinymce.sys.ini
/administrator/language/en-GB/en-GB.plg_editors-xtd_article.ini
/administrator/language/en-GB/en-GB.plg_editors-xtd_article.sys.ini
/administrator/language/en-GB/en-GB.plg_editors-xtd_contact.ini
@@ -422,12 +415,8 @@ src/media/js/user.js
/administrator/language/en-GB/en-GB.plg_editors-xtd_pagebreak.sys.ini
/administrator/language/en-GB/en-GB.plg_editors-xtd_readmore.ini
/administrator/language/en-GB/en-GB.plg_editors-xtd_readmore.sys.ini
/administrator/language/en-GB/en-GB.plg_editors_codemirror.ini
/administrator/language/en-GB/en-GB.plg_editors_codemirror.sys.ini
/administrator/language/en-GB/en-GB.plg_editors_none.ini
/administrator/language/en-GB/en-GB.plg_editors_none.sys.ini
/administrator/language/en-GB/en-GB.plg_editors_tinymce.ini
/administrator/language/en-GB/en-GB.plg_editors_tinymce.sys.ini
/administrator/language/en-GB/en-GB.plg_extension_example.ini
/administrator/language/en-GB/en-GB.plg_extension_example.sys.ini
/administrator/language/en-GB/en-GB.plg_extension_joomla.ini
/administrator/language/en-GB/en-GB.plg_extension_joomla.sys.ini
/administrator/language/en-GB/en-GB.plg_fields_calendar.ini
@@ -438,8 +427,6 @@ src/media/js/user.js
/administrator/language/en-GB/en-GB.plg_fields_color.sys.ini
/administrator/language/en-GB/en-GB.plg_fields_editor.ini
/administrator/language/en-GB/en-GB.plg_fields_editor.sys.ini
/administrator/language/en-GB/en-GB.plg_fields_image.ini
/administrator/language/en-GB/en-GB.plg_fields_image.sys.ini
/administrator/language/en-GB/en-GB.plg_fields_imagelist.ini
/administrator/language/en-GB/en-GB.plg_fields_imagelist.sys.ini
/administrator/language/en-GB/en-GB.plg_fields_integer.ini
@@ -474,16 +461,12 @@ src/media/js/user.js
/administrator/language/en-GB/en-GB.plg_finder_newsfeeds.sys.ini
/administrator/language/en-GB/en-GB.plg_finder_tags.ini
/administrator/language/en-GB/en-GB.plg_finder_tags.sys.ini
/administrator/language/en-GB/en-GB.plg_finder_weblinks.ini
/administrator/language/en-GB/en-GB.plg_finder_weblinks.sys.ini
/administrator/language/en-GB/en-GB.plg_installer_folderinstaller.ini
/administrator/language/en-GB/en-GB.plg_installer_folderinstaller.sys.ini
/administrator/language/en-GB/en-GB.plg_installer_packageinstaller.ini
/administrator/language/en-GB/en-GB.plg_installer_packageinstaller.sys.ini
/administrator/language/en-GB/en-GB.plg_installer_urlinstaller.ini
/administrator/language/en-GB/en-GB.plg_installer_urlinstaller.sys.ini
/administrator/language/en-GB/en-GB.plg_installer_webinstaller.ini
/administrator/language/en-GB/en-GB.plg_installer_webinstaller.sys.ini
/administrator/language/en-GB/en-GB.plg_privacy_actionlogs.ini
/administrator/language/en-GB/en-GB.plg_privacy_actionlogs.sys.ini
/administrator/language/en-GB/en-GB.plg_privacy_consents.ini
@@ -560,29 +543,22 @@ src/media/js/user.js
/administrator/language/en-GB/en-GB.plg_twofactorauth_yubikey.sys.ini
/administrator/language/en-GB/en-GB.plg_user_contactcreator.ini
/administrator/language/en-GB/en-GB.plg_user_contactcreator.sys.ini
/administrator/language/en-GB/en-GB.plg_user_example.ini
/administrator/language/en-GB/en-GB.plg_user_example.sys.ini
/administrator/language/en-GB/en-GB.plg_user_joomla.ini
/administrator/language/en-GB/en-GB.plg_user_joomla.sys.ini
/administrator/language/en-GB/en-GB.plg_user_profile.ini
/administrator/language/en-GB/en-GB.plg_user_profile.sys.ini
/administrator/language/en-GB/en-GB.plg_user_terms.ini
/administrator/language/en-GB/en-GB.plg_user_terms.sys.ini
/administrator/language/en-GB/en-GB.tpl_hathor.ini
/administrator/language/en-GB/en-GB.tpl_hathor.sys.ini
/administrator/language/en-GB/en-GB.tpl_isis.ini
/administrator/language/en-GB/en-GB.tpl_isis.sys.ini
/administrator/language/en-GB/en-GB.xml
/administrator/language/en-GB/install.xml
/administrator/language/overrides/*
/administrator/language/en-GB/index.html
/administrator/language/index.html
/administrator/language/overrides/*
/administrator/logs/*
/administrator/manifests/files/joomla.xml
/administrator/manifests/libraries/fof.xml
/administrator/manifests/libraries/idna_convert.xml
/administrator/manifests/libraries/joomla.xml
/administrator/manifests/libraries/phpass.xml
/administrator/manifests/libraries/phputf8.xml
/administrator/manifests/packages/pkg_en-GB.xml
/administrator/manifests/packages/index.html
/administrator/manifests/files/*
/administrator/manifests/libraries/*
/administrator/manifests/packages/*
/administrator/modules/mod_custom/*
/administrator/modules/mod_feed/*
/administrator/modules/mod_latest/*
@@ -619,21 +595,18 @@ src/media/js/user.js
/components/com_finder/*
/components/com_mailto/*
/components/com_media/*
/components/com_menus/*
/components/com_modules/*
/components/com_newsfeeds/*
/components/com_privacy/*
/components/com_search/*
/components/com_tags/*
/components/com_users/*
/components/com_weblinks/*
/components/com_wrapper/*
/components/index.html
/images/banners/*
/images/headers/*
/images/joomla*
/images/sampledata/*
/images/index.html
/images/joomla*
/images/powered_by.png
/includes/*
/installation/*
/language/en-GB/en-GB.com_ajax.ini
@@ -652,14 +625,12 @@ src/media/js/user.js
/language/en-GB/en-GB.com_weblinks.ini
/language/en-GB/en-GB.com_wrapper.ini
/language/en-GB/en-GB.files_joomla.sys.ini
/language/en-GB/en-GB.finder_cli.ini
/language/en-GB/en-GB.ini
/language/en-GB/en-GB.lib_fof.ini
/language/en-GB/en-GB.lib_fof.sys.ini
/language/en-GB/en-GB.lib_idna_convert.sys.ini
/language/en-GB/en-GB.lib_joomla.ini
/language/en-GB/en-GB.lib_joomla.sys.ini
/language/en-GB/en-GB.lib_phpass.sys.ini
/language/en-GB/en-GB.lib_phpmailer.sys.ini
/language/en-GB/en-GB.lib_phputf8.sys.ini
/language/en-GB/en-GB.lib_simplepie.sys.ini
@@ -727,13 +698,10 @@ src/media/js/user.js
/language/en-GB/en-GB.tpl_protostar.ini
/language/en-GB/en-GB.tpl_protostar.sys.ini
/language/en-GB/en-GB.xml
/language/en-GB/install.xml
/language/overrides/*
/language/en-GB/index.html
/language/index.html
/language/overrides/*
/layouts/joomla/*
/layouts/libraries/*
/layouts/plugins/*
/layouts/index.html
/libraries/cms/*
/libraries/fof/*
/libraries/idna_convert/*
@@ -744,7 +712,6 @@ src/media/js/user.js
/libraries/phpmailer/*
/libraries/phputf8/*
/libraries/simplepie/*
/libraries/src/*
/libraries/vendor/*
/libraries/classmap.php
/libraries/cms.php
@@ -760,8 +727,14 @@ src/media/js/user.js
/media/com_fields/*
/media/com_finder/*
/media/com_joomlaupdate/*
/media/com_mailto/*
/media/com_media/*
/media/com_menus/*
/media/com_modules/*
/media/com_newsfeeds/*
/media/com_privacy/*
/media/com_tags/*
/media/com_weblinks/*
/media/com_wrapper/*
/media/contacts/*
/media/editors/*
@@ -769,18 +742,18 @@ src/media/js/user.js
/media/mailto/*
/media/media/*
/media/mod_languages/*
/media/mod_sampledata/*
/media/overrider/*
/media/plg_captcha_recaptcha/*
/media/plg_captcha_recaptcha_invisible/*
/media/plg_editors-xtd_article/*
/media/plg_quickicon_extensionupdate/*
/media/plg_quickicon_joomlaupdate/*
/media/plg_quickicon_privacycheck/*
/media/plg_system_debug/*
/media/plg_system_highlight/*
/media/plg_system_stats/*
/media/plg_twofactorauth_totp/*
/media/system/*
/media/index.html
/modules/mod_articles_archive/*
/modules/mod_articles_categories/*
/modules/mod_articles_category/*
@@ -804,9 +777,9 @@ src/media/js/user.js
/modules/mod_tags_popular/*
/modules/mod_tags_similar/*
/modules/mod_users_latest/*
/modules/mod_weblinks/*
/modules/mod_whosonline/*
/modules/mod_wrapper/*
/modules/index.html
/plugins/actionlog/joomla/*
/plugins/authentication/cookie/*
/plugins/authentication/example/*

20
.moko-standards Normal file
View File

@@ -0,0 +1,20 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
# SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
# DEFGROUP: MokoStandards.Templates.Config
# INGROUP: MokoStandards.Templates
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
# PATH: /templates/configs/moko-standards.yml
# VERSION: 04.00.04
# BRIEF: Governance attachment template — synced to .moko-standards in every governed repository
# NOTE: Tokens replaced at sync time: mokoconsulting-tech, MokoCassiopeia, waas-component, 04.00.04
#
# This file is managed automatically by MokoStandards bulk sync.
# Do not edit manually — changes will be overwritten on the next sync.
# To update governance settings, open a PR in MokoStandards instead:
# https://github.com/mokoconsulting-tech/MokoStandards
standards_source: "https://github.com/mokoconsulting-tech/MokoStandards"
standards_version: "04.00.04"
platform: "waas-component"
governed_repo: "mokoconsulting-tech/MokoCassiopeia"

13
LICENSE
View File

@@ -6,17 +6,18 @@
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
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 (./LICENSE).
# FILE INFORMATION
DEFGROUP:
INGROUP: Project.Documentation
REPO:
VERSION: 03.05.00
DEFGROUP: MokoStandards
INGROUP: MokoStandards.Documentation
REPO: https://github.com/mokoconsulting-tech/MokoStandards/
VERSION: 05.00.00
PATH: ./LICENSE
BRIEF: Project license (GPL-3.0-or-later)
NOTE: Exact text fetched from gnu.org
-->
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
@@ -660,7 +661,7 @@ the "copyright" line and a pointer to where the full notice is found.
(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
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.

333
Makefile Normal file
View File

@@ -0,0 +1,333 @@
# Makefile for Joomla Extensions
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This is a reference Makefile for building Joomla extensions.
# Copy this to your repository root as "Makefile" and customize as needed.
#
# Supports: Modules, Plugins, Components, Packages, Templates
# ==============================================================================
# CONFIGURATION - Customize these for your extension
# ==============================================================================
# Extension Configuration
EXTENSION_NAME := mokoexample
EXTENSION_TYPE := module
# Options: module, plugin, component, package, template
EXTENSION_VERSION := 1.0.0
# Module Configuration (for modules only)
MODULE_TYPE := site
# Options: site, admin
# Plugin Configuration (for plugins only)
PLUGIN_GROUP := system
# Options: system, content, user, authentication, etc.
# Directories
SRC_DIR := .
BUILD_DIR := build
DIST_DIR := dist
DOCS_DIR := docs
# Joomla Installation (for local testing - customize paths)
JOOMLA_ROOT := /var/www/html/joomla
JOOMLA_VERSION := 4
# Tools
PHP := php
COMPOSER := composer
NPM := npm
PHPCS := vendor/bin/phpcs
PHPCBF := vendor/bin/phpcbf
PHPUNIT := vendor/bin/phpunit
ZIP := zip
# Coding Standards
PHPCS_STANDARD := Joomla
# Colors for output
COLOR_RESET := \033[0m
COLOR_GREEN := \033[32m
COLOR_YELLOW := \033[33m
COLOR_BLUE := \033[34m
COLOR_RED := \033[31m
# ==============================================================================
# TARGETS
# ==============================================================================
.PHONY: help
help: ## Show this help message
@echo "$(COLOR_BLUE)╔════════════════════════════════════════════════════════════╗$(COLOR_RESET)"
@echo "$(COLOR_BLUE)║ Joomla Extension Makefile ║$(COLOR_RESET)"
@echo "$(COLOR_BLUE)╚════════════════════════════════════════════════════════════╝$(COLOR_RESET)"
@echo ""
@echo "Extension: $(EXTENSION_NAME) ($(EXTENSION_TYPE)) v$(EXTENSION_VERSION)"
@echo ""
@echo "$(COLOR_GREEN)Available targets:$(COLOR_RESET)"
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " $(COLOR_BLUE)%-20s$(COLOR_RESET) %s\n", $$1, $$2}'
@echo ""
@echo "$(COLOR_YELLOW)Quick Start:$(COLOR_RESET)"
@echo " 1. make install-deps # Install dependencies"
@echo " 2. make build # Build extension package"
@echo " 3. make test # Run tests"
@echo ""
.PHONY: install-deps
install-deps: ## Install all dependencies (Composer + npm)
@echo "$(COLOR_BLUE)Installing dependencies...$(COLOR_RESET)"
@if [ -f "composer.json" ]; then \
$(COMPOSER) install; \
echo "$(COLOR_GREEN)✓ Composer dependencies installed$(COLOR_RESET)"; \
fi
@if [ -f "package.json" ]; then \
$(NPM) install; \
echo "$(COLOR_GREEN)✓ npm dependencies installed$(COLOR_RESET)"; \
fi
.PHONY: update-deps
update-deps: ## Update all dependencies
@echo "$(COLOR_BLUE)Updating dependencies...$(COLOR_RESET)"
@if [ -f "composer.json" ]; then \
$(COMPOSER) update; \
echo "$(COLOR_GREEN)✓ Composer dependencies updated$(COLOR_RESET)"; \
fi
@if [ -f "package.json" ]; then \
$(NPM) update; \
echo "$(COLOR_GREEN)✓ npm dependencies updated$(COLOR_RESET)"; \
fi
.PHONY: lint
lint: ## Run PHP linter (syntax check)
@echo "$(COLOR_BLUE)Running PHP linter...$(COLOR_RESET)"
@find . -name "*.php" ! -path "./vendor/*" ! -path "./node_modules/*" ! -path "./$(BUILD_DIR)/*" \
-exec $(PHP) -l {} \; | grep -v "No syntax errors" || true
@echo "$(COLOR_GREEN)✓ PHP linting complete$(COLOR_RESET)"
.PHONY: phpcs
phpcs: ## Run PHP CodeSniffer (Joomla standards)
@echo "$(COLOR_BLUE)Running PHP CodeSniffer...$(COLOR_RESET)"
@if [ -f "$(PHPCS)" ]; then \
$(PHPCS) --standard=$(PHPCS_STANDARD) --extensions=php --ignore=vendor,node_modules,$(BUILD_DIR) .; \
else \
echo "$(COLOR_YELLOW)⚠ PHP CodeSniffer not installed. Run: make install-deps$(COLOR_RESET)"; \
fi
.PHONY: phpcbf
phpcbf: ## Fix coding standards automatically
@echo "$(COLOR_BLUE)Running PHP Code Beautifier...$(COLOR_RESET)"
@if [ -f "$(PHPCBF)" ]; then \
$(PHPCBF) --standard=$(PHPCS_STANDARD) --extensions=php --ignore=vendor,node_modules,$(BUILD_DIR) .; \
echo "$(COLOR_GREEN)✓ Code formatting applied$(COLOR_RESET)"; \
else \
echo "$(COLOR_YELLOW)⚠ PHP Code Beautifier not installed. Run: make install-deps$(COLOR_RESET)"; \
fi
.PHONY: validate
validate: lint phpcs ## Run all validation checks
@echo "$(COLOR_GREEN)✓ All validation checks passed$(COLOR_RESET)"
.PHONY: test
test: ## Run PHPUnit tests
@echo "$(COLOR_BLUE)Running tests...$(COLOR_RESET)"
@if [ -f "$(PHPUNIT)" ] && [ -f "phpunit.xml" ]; then \
$(PHPUNIT); \
else \
echo "$(COLOR_YELLOW)⚠ PHPUnit not configured$(COLOR_RESET)"; \
fi
.PHONY: test-coverage
test-coverage: ## Run tests with coverage report
@echo "$(COLOR_BLUE)Running tests with coverage...$(COLOR_RESET)"
@if [ -f "$(PHPUNIT)" ] && [ -f "phpunit.xml" ]; then \
$(PHPUNIT) --coverage-html $(BUILD_DIR)/coverage; \
echo "$(COLOR_GREEN)✓ Coverage report: $(BUILD_DIR)/coverage/index.html$(COLOR_RESET)"; \
else \
echo "$(COLOR_YELLOW)⚠ PHPUnit not configured$(COLOR_RESET)"; \
fi
.PHONY: clean
clean: ## Clean build artifacts
@echo "$(COLOR_BLUE)Cleaning build artifacts...$(COLOR_RESET)"
@rm -rf $(BUILD_DIR) $(DIST_DIR)
@echo "$(COLOR_GREEN)✓ Build artifacts cleaned$(COLOR_RESET)"
.PHONY: build
build: clean validate ## Build extension package
@echo "$(COLOR_BLUE)Building Joomla extension package...$(COLOR_RESET)"
@mkdir -p $(DIST_DIR) $(BUILD_DIR)
# Determine package prefix based on extension type
@case "$(EXTENSION_TYPE)" in \
module) \
PACKAGE_PREFIX="mod_$(EXTENSION_NAME)"; \
BUILD_TARGET="$(BUILD_DIR)/$$PACKAGE_PREFIX"; \
;; \
plugin) \
PACKAGE_PREFIX="plg_$(PLUGIN_GROUP)_$(EXTENSION_NAME)"; \
BUILD_TARGET="$(BUILD_DIR)/$$PACKAGE_PREFIX"; \
;; \
component) \
PACKAGE_PREFIX="com_$(EXTENSION_NAME)"; \
BUILD_TARGET="$(BUILD_DIR)/$$PACKAGE_PREFIX"; \
;; \
package) \
PACKAGE_PREFIX="pkg_$(EXTENSION_NAME)"; \
BUILD_TARGET="$(BUILD_DIR)/$$PACKAGE_PREFIX"; \
;; \
template) \
PACKAGE_PREFIX="tpl_$(EXTENSION_NAME)"; \
BUILD_TARGET="$(BUILD_DIR)/$$PACKAGE_PREFIX"; \
;; \
*) \
echo "$(COLOR_RED)✗ Unknown extension type: $(EXTENSION_TYPE)$(COLOR_RESET)"; \
exit 1; \
;; \
esac; \
\
mkdir -p "$$BUILD_TARGET"; \
\
echo "Building $$PACKAGE_PREFIX..."; \
\
rsync -av --progress \
--exclude='$(BUILD_DIR)' \
--exclude='$(DIST_DIR)' \
--exclude='.git*' \
--exclude='vendor/' \
--exclude='node_modules/' \
--exclude='tests/' \
--exclude='Makefile' \
--exclude='composer.json' \
--exclude='composer.lock' \
--exclude='package.json' \
--exclude='package-lock.json' \
--exclude='phpunit.xml' \
--exclude='*.md' \
--exclude='.editorconfig' \
. "$$BUILD_TARGET/"; \
\
cd $(BUILD_DIR) && $(ZIP) -r "../$(DIST_DIR)/$${PACKAGE_PREFIX}-$(EXTENSION_VERSION).zip" "$${PACKAGE_PREFIX}"; \
\
echo "$(COLOR_GREEN)✓ Package created: $(DIST_DIR)/$${PACKAGE_PREFIX}-$(EXTENSION_VERSION).zip$(COLOR_RESET)"
.PHONY: package
package: build ## Alias for build
@echo "$(COLOR_GREEN)✓ Package ready for distribution$(COLOR_RESET)"
.PHONY: install-local
install-local: build ## Install to local Joomla (upload via admin)
@echo "$(COLOR_BLUE)Package ready for installation$(COLOR_RESET)"
@case "$(EXTENSION_TYPE)" in \
module) PACKAGE="mod_$(EXTENSION_NAME)";; \
plugin) PACKAGE="plg_$(PLUGIN_GROUP)_$(EXTENSION_NAME)";; \
component) PACKAGE="com_$(EXTENSION_NAME)";; \
package) PACKAGE="pkg_$(EXTENSION_NAME)";; \
template) PACKAGE="tpl_$(EXTENSION_NAME)";; \
esac; \
echo "$(COLOR_YELLOW)Upload $(DIST_DIR)/$${PACKAGE}-$(EXTENSION_VERSION).zip via Joomla Administrator$(COLOR_RESET)"; \
echo "Admin URL: $(JOOMLA_ROOT) → Extensions → Install"
.PHONY: dev-install
dev-install: ## Create symlink for development (Joomla 4+)
@echo "$(COLOR_BLUE)Creating development symlink...$(COLOR_RESET)"
@if [ ! -d "$(JOOMLA_ROOT)" ]; then \
echo "$(COLOR_RED)✗ Joomla root not found at $(JOOMLA_ROOT)$(COLOR_RESET)"; \
echo "Update JOOMLA_ROOT in Makefile"; \
exit 1; \
fi
@case "$(EXTENSION_TYPE)" in \
module) \
if [ "$(MODULE_TYPE)" = "admin" ]; then \
TARGET="$(JOOMLA_ROOT)/administrator/modules/mod_$(EXTENSION_NAME)"; \
else \
TARGET="$(JOOMLA_ROOT)/modules/mod_$(EXTENSION_NAME)"; \
fi; \
;; \
plugin) \
TARGET="$(JOOMLA_ROOT)/plugins/$(PLUGIN_GROUP)/$(EXTENSION_NAME)"; \
;; \
component) \
echo "$(COLOR_YELLOW)⚠ Components require complex symlink setup$(COLOR_RESET)"; \
echo "Manual setup recommended for component development"; \
exit 1; \
;; \
*) \
echo "$(COLOR_RED)✗ dev-install not supported for $(EXTENSION_TYPE)$(COLOR_RESET)"; \
exit 1; \
;; \
esac; \
\
rm -rf "$$TARGET"; \
ln -s "$(PWD)" "$$TARGET"; \
echo "$(COLOR_GREEN)✓ Development symlink created at $$TARGET$(COLOR_RESET)"
.PHONY: watch
watch: ## Watch for changes and rebuild
@echo "$(COLOR_BLUE)Watching for changes...$(COLOR_RESET)"
@echo "$(COLOR_YELLOW)Press Ctrl+C to stop$(COLOR_RESET)"
@while true; do \
inotifywait -r -e modify,create,delete --exclude '($(BUILD_DIR)|$(DIST_DIR)|vendor|node_modules)' . 2>/dev/null || \
(echo "$(COLOR_YELLOW)⚠ inotifywait not installed. Install: apt-get install inotify-tools$(COLOR_RESET)" && sleep 5); \
make build; \
done
.PHONY: version
version: ## Display version information
@echo "$(COLOR_BLUE)Extension Information:$(COLOR_RESET)"
@echo " Name: $(EXTENSION_NAME)"
@echo " Type: $(EXTENSION_TYPE)"
@echo " Version: $(EXTENSION_VERSION)"
@if [ "$(EXTENSION_TYPE)" = "module" ]; then \
echo " Module: $(MODULE_TYPE)"; \
fi
@if [ "$(EXTENSION_TYPE)" = "plugin" ]; then \
echo " Group: $(PLUGIN_GROUP)"; \
fi
.PHONY: docs
docs: ## Generate documentation
@echo "$(COLOR_BLUE)Generating documentation...$(COLOR_RESET)"
@mkdir -p $(DOCS_DIR)
@echo "$(COLOR_YELLOW)⚠ Documentation generation not configured$(COLOR_RESET)"
@echo "Consider adding phpDocumentor or similar"
.PHONY: release
release: validate test build ## Create a release (validate + test + build)
@echo "$(COLOR_GREEN)✓ Release package ready$(COLOR_RESET)"
@echo ""
@echo "$(COLOR_BLUE)Release Checklist:$(COLOR_RESET)"
@echo " [ ] Update CHANGELOG.md"
@echo " [ ] Update version in XML manifest"
@echo " [ ] Test installation in clean Joomla"
@echo " [ ] Tag release in git: git tag v$(EXTENSION_VERSION)"
@echo " [ ] Push tags: git push --tags"
@echo " [ ] Create GitHub release"
@echo ""
@case "$(EXTENSION_TYPE)" in \
module) PACKAGE="mod_$(EXTENSION_NAME)";; \
plugin) PACKAGE="plg_$(PLUGIN_GROUP)_$(EXTENSION_NAME)";; \
component) PACKAGE="com_$(EXTENSION_NAME)";; \
package) PACKAGE="pkg_$(EXTENSION_NAME)";; \
template) PACKAGE="tpl_$(EXTENSION_NAME)";; \
esac; \
echo "$(COLOR_GREEN)Package: $(DIST_DIR)/$${PACKAGE}-$(EXTENSION_VERSION).zip$(COLOR_RESET)"
.PHONY: security-check
security-check: ## Run security checks on dependencies
@echo "$(COLOR_BLUE)Running security checks...$(COLOR_RESET)"
@if [ -f "composer.json" ]; then \
$(COMPOSER) audit || echo "$(COLOR_YELLOW)⚠ Vulnerabilities found$(COLOR_RESET)"; \
fi
@if [ -f "package.json" ]; then \
$(NPM) audit || echo "$(COLOR_YELLOW)⚠ Vulnerabilities found$(COLOR_RESET)"; \
fi
.PHONY: all
all: install-deps validate test build ## Run complete build pipeline
@echo "$(COLOR_GREEN)✓ Complete build pipeline finished$(COLOR_RESET)"
# Default target
.DEFAULT_GOAL := help

55
composer.json Normal file
View File

@@ -0,0 +1,55 @@
{
"name": "mokoconsulting-tech/mokocassiopeia",
"description": "MokoCassiopeia Joomla component by Moko Consulting",
"type": "joomla-component",
"version": "01.00.00",
"license": "GPL-3.0-or-later",
"authors": [
{
"name": "Moko Consulting",
"email": "hello@mokoconsulting.tech"
}
],
"minimum-stability": "stable",
"prefer-stable": true,
"require": {
"php": ">=8.1",
"mokoconsulting-tech/enterprise": "^4.0"
},
"require-dev": {
"phpunit/phpunit": "^10.5",
"phpstan/phpstan": "^2.0",
"squizlabs/php_codesniffer": "^4.0"
},
"autoload": {
"psr-4": {
"MokoConsulting\\MokoCassiopeia\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"MokoConsulting\\MokoCassiopeia\\Tests\\": "tests/"
}
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/mokoconsulting-tech/MokoStandards"
}
],
"config": {
"sort-packages": true,
"optimize-autoloader": true,
"preferred-install": "dist",
"allow-plugins": {
"composer/installers": true
}
},
"scripts": {
"validate-manifest": "vendor/bin/validate-manifest --path .",
"build": "vendor/bin/build-package --path .",
"test": "phpunit",
"phpcs": "phpcs --standard=vendor/mokoconsulting-tech/enterprise/phpcs.xml src/",
"phpstan": "phpstan analyse -c phpstan.neon src/"
}
}

View File

@@ -1,37 +1,32 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
# SPDX-License-Identifier: GPL-3.0-or-later
#
# PHPStan configuration for Joomla extension repositories.
# Extends the base MokoStandards config and adds Joomla framework class stubs
# so PHPStan can resolve Factory, CMSApplication, User, Table, etc.
# without requiring a full Joomla installation.
parameters:
level: 5
paths:
- src
# Exclude paths
excludePaths:
- src/vendor/*
- src/node_modules/*
- src/cache/*
- src/tmp/*
- vendor
- node_modules
# Scan files
scanFiles:
- src/templates/index.php
- src/templates/component.php
- src/templates/error.php
- src/templates/offline.php
# Joomla framework stubs — resolved via the enterprise package from vendor/
stubFiles:
- vendor/mokoconsulting-tech/enterprise/templates/stubs/joomla.php
# Report unmatched ignored errors
reportUnmatchedIgnoredErrors: false
# Check function name case
checkFunctionNameCase: true
# Check internal classes
checkInternalClassCaseSensitivity: true
# Treat PHP version
phpVersion: 80000
# Ignore errors - adjust as needed
# Suppress errors that are structural in Joomla's service-container architecture
ignoreErrors:
# Allow dynamic properties which are common in Joomla
- '#Access to an undefined property#'
# Allow some reflection usage
- '#Call to an undefined static method#'
# Joomla's service-based dependency injection returns mixed from getApplication()
- '#Cannot call method .+ on Joomla\\CMS\\Application\\CMSApplication\|null#'
# Factory::getX() patterns are safe at runtime even when nullable in stubs
- '#Call to static method [a-zA-Z]+\(\) on an interface#'
reportUnmatchedIgnoredErrors: false
checkMissingIterableValueType: false
checkGenericClassInNonGenericObjectType: false

34
update.xml Normal file
View File

@@ -0,0 +1,34 @@
<!--
Joomla Extension Update Server XML
See: https://docs.joomla.org/Deploying_an_Update_Server
This file is the update server manifest for {{EXTENSION_NAME}}.
The Joomla installer polls this URL to check for new versions.
The manifest.xml in this repository must reference this file:
<updateservers>
<server type="extension" priority="1" name="{{EXTENSION_NAME}}">
https://github.com/mokoconsulting-tech/MokoCassiopeia/raw/main/update.xml
</server>
</updateservers>
When a new release is made, run `make release` or the release workflow to
prepend a new <update> entry to this file automatically.
-->
<updates>
<update>
<name>{{EXTENSION_NAME}}</name>
<description>MokoCassiopeia — Moko Consulting Joomla extension</description>
<element>{{EXTENSION_ELEMENT}}</element>
<type>{{EXTENSION_TYPE}}</type>
<version>{{VERSION}}</version>
<infourl title="Release Information">https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/tag/{{VERSION}}</infourl>
<downloads>
<downloadurl type="full" format="zip">{{DOWNLOAD_URL}}</downloadurl>
</downloads>
<targetplatform name="joomla" version="4\.[0-9]+" />
<php_minimum>7.4</php_minimum>
<maintainer>Moko Consulting</maintainer>
<maintainerurl>{{MAINTAINER_URL}}</maintainerurl>
</update>
</updates>