Reorganization
This commit is contained in:
92
.github/workflows/build.yml
vendored
Normal file
92
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
name: Build ZIP from src
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [prereleased, released]
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
source_dir:
|
||||||
|
description: "Folder to zip (relative to repo root)"
|
||||||
|
required: false
|
||||||
|
default: "src"
|
||||||
|
zip_prefix:
|
||||||
|
description: "ZIP filename prefix (default repo name)"
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
zip_name:
|
||||||
|
description: "Override full ZIP filename (without .zip)"
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
build-zip:
|
||||||
|
name: Build ZIP from src
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
env:
|
||||||
|
DEFAULT_SOURCE_DIR: src
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Resolve build parameters
|
||||||
|
id: cfg
|
||||||
|
run: |
|
||||||
|
if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ -n "${{ github.event.inputs.source_dir }}" ]; then
|
||||||
|
SRC_DIR="${{ github.event.inputs.source_dir }}"
|
||||||
|
else
|
||||||
|
SRC_DIR="${DEFAULT_SOURCE_DIR}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ -n "${{ github.event.inputs.zip_prefix }}" ]; then
|
||||||
|
PREFIX="${{ github.event.inputs.zip_prefix }}"
|
||||||
|
else
|
||||||
|
PREFIX="${GITHUB_REPOSITORY##*/}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${{ github.event_name }}" = "release" ]; then
|
||||||
|
REF_NAME="${GITHUB_REF_NAME}"
|
||||||
|
else
|
||||||
|
REF_NAME="${GITHUB_SHA::7}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ -n "${{ github.event.inputs.zip_name }}" ]; then
|
||||||
|
ZIP_NAME="${{ github.event.inputs.zip_name }}"
|
||||||
|
else
|
||||||
|
ZIP_NAME="${PREFIX}-${REF_NAME}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "src_dir=${SRC_DIR}" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Validate source directory
|
||||||
|
run: |
|
||||||
|
if [ ! -d "${{ steps.cfg.outputs.src_dir }}" ]; then
|
||||||
|
echo "Source directory '${{ steps.cfg.outputs.src_dir }}' does not exist."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Prepare dist folder
|
||||||
|
run: |
|
||||||
|
mkdir -p dist
|
||||||
|
|
||||||
|
- name: Build ZIP from folder contents
|
||||||
|
run: |
|
||||||
|
cd "${{ steps.cfg.outputs.src_dir }}"
|
||||||
|
zip -r "../dist/${{ steps.cfg.outputs.zip_name }}.zip" .
|
||||||
|
|
||||||
|
- name: Upload artifact to workflow run
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ steps.cfg.outputs.zip_name }}
|
||||||
|
path: dist/${{ steps.cfg.outputs.zip_name }}.zip
|
||||||
|
|
||||||
|
- name: Attach ZIP to GitHub Release
|
||||||
|
if: github.event_name == 'release'
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
files: dist/${{ steps.cfg.outputs.zip_name }}.zip
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
163
.github/workflows/ci.yml
vendored
163
.github/workflows/ci.yml
vendored
@@ -1,45 +1,132 @@
|
|||||||
# Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
|
name: Continuous Integration Pipeline
|
||||||
#
|
|
||||||
# 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 (./LICENSE.md).
|
|
||||||
#
|
|
||||||
# FILE INFORMATION
|
|
||||||
# DEFGROUP: # INGROUP: Documentation
|
|
||||||
# REPO: # FILE: ci.yml
|
|
||||||
# VERSION: # BRIEF: Baseline CI workflow
|
|
||||||
# PATH: ./.github/workflows/ci.yml
|
|
||||||
# NOTE: name: ci
|
|
||||||
|
|
||||||
on: push: branches: ["main"]
|
on:
|
||||||
pull_request: branches: ["main"]
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- "version/*"
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
jobs: lint-and-test: runs-on: ubuntu-latest
|
permissions:
|
||||||
steps: - name: Checkout
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
php-lint:
|
||||||
|
name: PHP lint
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Node
|
- name: Set up PHP
|
||||||
uses: actions/setup-node@v4
|
uses: shivammathur/setup-php@v2
|
||||||
with: node-version: "20"
|
with:
|
||||||
|
php-version: "8.2"
|
||||||
|
coverage: none
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Lint PHP files under src
|
||||||
run: npm ci
|
run: |
|
||||||
continue-on-error: true
|
if [ -d "src" ]; then
|
||||||
|
echo "Running php -l against PHP files in src/"
|
||||||
|
find src -type f -name "*.php" -print0 | xargs -0 -n 1 -P 4 php -l
|
||||||
|
else
|
||||||
|
echo "No src directory found. Skipping PHP lint."
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Lint
|
composer-tests:
|
||||||
run: npm run lint
|
name: Composer install and tests
|
||||||
continue-on-error: true
|
runs-on: ubuntu-latest
|
||||||
|
needs: php-lint
|
||||||
|
|
||||||
- name: Test
|
steps:
|
||||||
run: npm test
|
- name: Check out repository
|
||||||
continue-on-error: true
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up PHP
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: "8.2"
|
||||||
|
coverage: none
|
||||||
|
|
||||||
|
- name: Install dependencies if composer.json exists
|
||||||
|
run: |
|
||||||
|
if [ -f "composer.json" ]; then
|
||||||
|
composer install --no-interaction --no-progress --prefer-dist
|
||||||
|
else
|
||||||
|
echo "No composer.json found. Skipping dependency install and tests."
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Run Composer tests when defined
|
||||||
|
run: |
|
||||||
|
if [ ! -f "composer.json" ]; then
|
||||||
|
echo "No composer.json. Nothing to test."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if composer run -q | grep -q "^ test"; then
|
||||||
|
echo "Detected composer script 'test'. Running composer test."
|
||||||
|
composer test
|
||||||
|
else
|
||||||
|
echo "No 'test' script defined in composer.json. Skipping tests."
|
||||||
|
fi
|
||||||
|
|
||||||
|
validate-manifest-and-changelog:
|
||||||
|
name: Validate manifest and changelog via scripts
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- php-lint
|
||||||
|
- composer-tests
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Run manifest validation script if present
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
echo "Checking for manifest validation scripts"
|
||||||
|
|
||||||
|
if [ -x "scripts/validate_manifest.sh" ]; then
|
||||||
|
echo "Running scripts/validate_manifest.sh"
|
||||||
|
scripts/validate_manifest.sh
|
||||||
|
elif [ -f "scripts/validate_manifest.php" ]; then
|
||||||
|
echo "Running scripts/validate_manifest.php"
|
||||||
|
php scripts/validate_manifest.php
|
||||||
|
elif [ -f "scripts/validate_manifest.py" ]; then
|
||||||
|
echo "Running scripts/validate_manifest.py"
|
||||||
|
python scripts/validate_manifest.py
|
||||||
|
else
|
||||||
|
echo "No manifest validation script found (scripts/validate_manifest.*). Skipping manifest validation step."
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Run changelog update/verification script if present
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
echo "Checking for changelog update or verification scripts"
|
||||||
|
|
||||||
|
if [ -x "scripts/update_changelog.sh" ]; then
|
||||||
|
echo "Running scripts/update_changelog.sh --ci"
|
||||||
|
scripts/update_changelog.sh --ci
|
||||||
|
elif [ -f "scripts/update_changelog.py" ]; then
|
||||||
|
echo "Running scripts/update_changelog.py --ci"
|
||||||
|
python scripts/update_changelog.py --ci
|
||||||
|
elif [ -x "scripts/verify_changelog.sh" ]; then
|
||||||
|
echo "Running scripts/verify_changelog.sh"
|
||||||
|
scripts/verify_changelog.sh
|
||||||
|
else
|
||||||
|
echo "No changelog script found (scripts/update_changelog.* or scripts/verify_changelog.sh). Skipping changelog enforcement."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Checking for uncommitted changes after changelog script"
|
||||||
|
if ! git diff --quiet; then
|
||||||
|
echo "Changelog or related files were modified by the script."
|
||||||
|
echo "Please run the changelog script locally and commit the changes before pushing."
|
||||||
|
git diff
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|||||||
80
.github/workflows/cleanup_version_branches.yml
vendored
Normal file
80
.github/workflows/cleanup_version_branches.yml
vendored
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
name: Cleanup merged version branches
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 3 * * 0"
|
||||||
|
# Runs every Sunday at 03:00 UTC; adjust if needed.
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
cleanup-version-branches:
|
||||||
|
name: Delete merged version/* branches
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Fetch all remote branches
|
||||||
|
run: |
|
||||||
|
git fetch --all --prune
|
||||||
|
|
||||||
|
- name: Identify merged version/* branches
|
||||||
|
id: find
|
||||||
|
run: |
|
||||||
|
# List remote version branches
|
||||||
|
REMOTE_BRANCHES=$(git branch -r --list "origin/version/*" | sed 's/^ *//')
|
||||||
|
|
||||||
|
if [ -z "$REMOTE_BRANCHES" ]; then
|
||||||
|
echo "No remote version/* branches found."
|
||||||
|
echo "branches=" >> "$GITHUB_OUTPUT"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Found version branches:"
|
||||||
|
echo "$REMOTE_BRANCHES"
|
||||||
|
|
||||||
|
MERGED_BRANCHES=()
|
||||||
|
|
||||||
|
# Determine which are fully merged into origin/main
|
||||||
|
for RB in $REMOTE_BRANCHES; do
|
||||||
|
# Strip the 'origin/' prefix for local tracking
|
||||||
|
LB=${RB#origin/}
|
||||||
|
|
||||||
|
# Check merge status against origin/main
|
||||||
|
if git branch --remotes --merged origin/main | grep -q "$RB"; then
|
||||||
|
# Do not touch the currently checked out branch (defensive)
|
||||||
|
if [ "$LB" != "$GITHUB_REF_NAME" ]; then
|
||||||
|
MERGED_BRANCHES+=("$LB")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "${#MERGED_BRANCHES[@]}" -eq 0 ]; then
|
||||||
|
echo "No merged version/* branches to delete."
|
||||||
|
echo "branches=" >> "$GITHUB_OUTPUT"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Merged version branches to delete:"
|
||||||
|
printf '%s\n' "${MERGED_BRANCHES[@]}"
|
||||||
|
|
||||||
|
# Join into space-separated list for output
|
||||||
|
echo "branches=${MERGED_BRANCHES[*]}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Delete merged version branches on origin
|
||||||
|
if: steps.find.outputs.branches != ''
|
||||||
|
env:
|
||||||
|
BRANCHES: ${{ steps.find.outputs.branches }}
|
||||||
|
run: |
|
||||||
|
echo "Deleting merged version branches: ${BRANCHES}"
|
||||||
|
|
||||||
|
for BR in ${BRANCHES}; do
|
||||||
|
echo "Deleting origin/${BR}"
|
||||||
|
git push origin --delete "${BR}" || echo "Failed to delete ${BR} (might already be gone)"
|
||||||
|
done
|
||||||
23
.github/workflows/init.yml
vendored
Normal file
23
.github/workflows/init.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
- name: Initialize update server repository context
|
||||||
|
id: init
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Canonical repo identifiers
|
||||||
|
REPO_SLUG="${GITHUB_REPOSITORY}" # owner/repo
|
||||||
|
REPO_NAME="${GITHUB_REPOSITORY##*/}" # repo
|
||||||
|
SERVER_URL="${GITHUB_SERVER_URL:-https://github.com}"
|
||||||
|
|
||||||
|
# Update server XML source repository URL
|
||||||
|
UPDATE_XML_REPO_URL="${SERVER_URL}/${REPO_SLUG}"
|
||||||
|
|
||||||
|
echo "Resolved repository slug: ${REPO_SLUG}"
|
||||||
|
echo "Resolved repository name: ${REPO_NAME}"
|
||||||
|
echo "Resolved update XML repo URL: ${UPDATE_XML_REPO_URL}"
|
||||||
|
|
||||||
|
# Export for downstream steps
|
||||||
|
{
|
||||||
|
echo "repo_slug=${REPO_SLUG}"
|
||||||
|
echo "repo_name=${REPO_NAME}"
|
||||||
|
echo "update_xml_repo_url=${UPDATE_XML_REPO_URL}"
|
||||||
|
} >> "$GITHUB_OUTPUT"
|
||||||
278
.github/workflows/release_from_version.yml
vendored
Normal file
278
.github/workflows/release_from_version.yml
vendored
Normal file
@@ -0,0 +1,278 @@
|
|||||||
|
name: Version branch release pipeline
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
issues: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
meta:
|
||||||
|
name: Derive version metadata from branch
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
outputs:
|
||||||
|
branch: ${{ steps.meta.outputs.branch }}
|
||||||
|
version: ${{ steps.meta.outputs.version }}
|
||||||
|
is_prerelease: ${{ steps.meta.outputs.is_prerelease }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Determine branch and version
|
||||||
|
id: meta
|
||||||
|
run: |
|
||||||
|
BRANCH="${GITHUB_REF_NAME}"
|
||||||
|
|
||||||
|
echo "Running on branch: ${BRANCH}"
|
||||||
|
|
||||||
|
if [[ ! "${BRANCH}" =~ ^version\/[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9._]+)?$ ]]; then
|
||||||
|
echo "This workflow must be run on a branch named version/X.Y.Z or version/X.Y.Z-suffix"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
VERSION="${BRANCH#version/}"
|
||||||
|
|
||||||
|
echo "Detected version: ${VERSION}"
|
||||||
|
|
||||||
|
if [[ "${VERSION}" =~ -(alpha|beta|rc|pre|preview|dev|test) ]]; then
|
||||||
|
echo "Version is prerelease: ${VERSION}"
|
||||||
|
IS_PRERELEASE="true"
|
||||||
|
else
|
||||||
|
echo "Version is stable: ${VERSION}"
|
||||||
|
IS_PRERELEASE="false"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "branch=${BRANCH}" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "is_prerelease=${IS_PRERELEASE}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
build-and-test:
|
||||||
|
name: Build and test (sanity check)
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: meta
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out version branch
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ needs.meta.outputs.branch }}
|
||||||
|
|
||||||
|
- name: Set up PHP
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: "8.2"
|
||||||
|
coverage: none
|
||||||
|
|
||||||
|
- name: PHP lint under src (if present)
|
||||||
|
run: |
|
||||||
|
if [ -d "src" ]; then
|
||||||
|
echo "Running php -l against PHP files in src/"
|
||||||
|
find src -type f -name "*.php" -print0 | xargs -0 -n 1 -P 4 php -l
|
||||||
|
else
|
||||||
|
echo "No src directory found. Skipping PHP lint."
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Install dependencies if composer.json exists
|
||||||
|
run: |
|
||||||
|
if [ -f "composer.json" ]; then
|
||||||
|
composer install --no-interaction --no-progress --prefer-dist
|
||||||
|
else
|
||||||
|
echo "No composer.json found. Skipping composer install."
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Run Composer tests when defined
|
||||||
|
run: |
|
||||||
|
if [ ! -f "composer.json" ]; then
|
||||||
|
echo "No composer.json. Nothing to test."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if composer run -q | grep -q "^ test"; then
|
||||||
|
echo "Detected composer script 'test'. Running composer test."
|
||||||
|
composer test
|
||||||
|
else
|
||||||
|
echo "No 'test' script defined in composer.json. Skipping tests."
|
||||||
|
fi
|
||||||
|
|
||||||
|
changelog:
|
||||||
|
name: Update CHANGELOG.md on version branch
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [meta, build-and-test]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out version branch with history
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ needs.meta.outputs.branch }}
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Fetch main for comparison
|
||||||
|
run: |
|
||||||
|
git fetch origin main
|
||||||
|
|
||||||
|
- name: Update CHANGELOG using script
|
||||||
|
env:
|
||||||
|
VERSION: ${{ needs.meta.outputs.version }}
|
||||||
|
run: |
|
||||||
|
if [ ! -f "scripts/update_changelog.sh" ]; then
|
||||||
|
echo "ERROR: scripts/update_changelog.sh not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
chmod +x scripts/update_changelog.sh
|
||||||
|
./scripts/update_changelog.sh "${VERSION}"
|
||||||
|
|
||||||
|
- name: Commit CHANGELOG.md if changed
|
||||||
|
env:
|
||||||
|
VERSION: ${{ needs.meta.outputs.version }}
|
||||||
|
run: |
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
|
||||||
|
if git diff --quiet; then
|
||||||
|
echo "No changelog changes to commit."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
git add CHANGELOG.md
|
||||||
|
git commit -m "chore: update changelog for ${VERSION}"
|
||||||
|
git push origin HEAD
|
||||||
|
|
||||||
|
pr-merge-release:
|
||||||
|
name: PR, conditional squash, and GitHub release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [meta, changelog]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out version branch
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ needs.meta.outputs.branch }}
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Verify branch has commits ahead of main
|
||||||
|
run: |
|
||||||
|
git fetch origin main
|
||||||
|
|
||||||
|
AHEAD_COUNT=$(git rev-list --count origin/main..HEAD)
|
||||||
|
echo "Commits ahead of main: ${AHEAD_COUNT}"
|
||||||
|
|
||||||
|
if [ "${AHEAD_COUNT}" -eq 0 ]; then
|
||||||
|
echo "ERROR: No commits between main and ${GITHUB_REF_NAME}."
|
||||||
|
echo "Action required: commit changes to the version branch before running this workflow."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Ensure standard PR labels exist
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
echo "Ensuring standard labels exist"
|
||||||
|
gh label create "release" --color "0E8A16" --description "Release related PR" || echo "Label 'release' already exists"
|
||||||
|
gh label create "version-update" --color "5319E7" --description "Version bump and release PR" || echo "Label 'version-update' already exists"
|
||||||
|
|
||||||
|
- name: Create or reuse PR from version branch to main
|
||||||
|
id: pr
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
BRANCH: ${{ needs.meta.outputs.branch }}
|
||||||
|
VERSION: ${{ needs.meta.outputs.version }}
|
||||||
|
run: |
|
||||||
|
echo "Ensuring PR exists for ${BRANCH} -> main"
|
||||||
|
|
||||||
|
PR_NUMBER=$(gh pr list --head "${BRANCH}" --base "main" --state open --json number -q '.[0].number' || true)
|
||||||
|
|
||||||
|
if [ -z "${PR_NUMBER}" ]; then
|
||||||
|
echo "No existing open PR found. Creating PR."
|
||||||
|
PR_URL=$(gh pr create --base "main" --head "${BRANCH}" --title "Merge version ${VERSION} into main" --body "Automated PR to merge version ${VERSION} into main.")
|
||||||
|
PR_NUMBER=$(gh pr view "${PR_URL}" --json number -q '.number')
|
||||||
|
|
||||||
|
echo "Applying standard labels (non-blocking)"
|
||||||
|
gh pr edit "${PR_NUMBER}" --add-label "release" || echo "Label 'release' not found or cannot be applied"
|
||||||
|
gh pr edit "${PR_NUMBER}" --add-label "version-update" || echo "Label 'version-update' not found or cannot be applied"
|
||||||
|
else
|
||||||
|
echo "Found existing PR #${PR_NUMBER}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "pr_number=${PR_NUMBER}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Squash merge PR into main (stable only)
|
||||||
|
if: needs.meta.outputs.is_prerelease == 'false'
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
REPO: ${{ github.repository }}
|
||||||
|
VERSION: ${{ needs.meta.outputs.version }}
|
||||||
|
PR_NUMBER: ${{ steps.pr.outputs.pr_number }}
|
||||||
|
run: |
|
||||||
|
if [ -z "${PR_NUMBER}" ]; then
|
||||||
|
echo "No pull request number returned. Cannot squash merge."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Performing squash merge PR #${PR_NUMBER} into main"
|
||||||
|
|
||||||
|
MERGE_PAYLOAD=$(jq -n --arg method "squash" --arg title "Squash merge version ${VERSION} into main" '{"merge_method": $method, "commit_title": $title}')
|
||||||
|
|
||||||
|
curl -sS -X PUT -H "Authorization: Bearer ${GITHUB_TOKEN}" -H "Accept: application/vnd.github+json" "https://api.github.com/repos/${REPO}/pulls/${PR_NUMBER}/merge" -d "${MERGE_PAYLOAD}"
|
||||||
|
|
||||||
|
- name: Skip squash (prerelease detected)
|
||||||
|
if: needs.meta.outputs.is_prerelease == 'true'
|
||||||
|
run: |
|
||||||
|
echo "Prerelease version detected. PR created but squash merge intentionally skipped."
|
||||||
|
|
||||||
|
- name: Create GitHub Release (stable and prerelease) and attach ZIP from src
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
VERSION: ${{ needs.meta.outputs.version }}
|
||||||
|
IS_PRERELEASE: ${{ needs.meta.outputs.is_prerelease }}
|
||||||
|
run: |
|
||||||
|
PRERELEASE_FLAG="false"
|
||||||
|
if [ "${IS_PRERELEASE}" = "true" ]; then
|
||||||
|
PRERELEASE_FLAG="true"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Building ZIP from src for version ${VERSION}"
|
||||||
|
|
||||||
|
REPO_NAME="${GITHUB_REPOSITORY##*/}"
|
||||||
|
ASSET_NAME="${REPO_NAME}-${VERSION}.zip"
|
||||||
|
|
||||||
|
if [ ! -d "src" ]; then
|
||||||
|
echo "ERROR: src directory does not exist. Cannot build release ZIP."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p dist
|
||||||
|
cd src
|
||||||
|
zip -r "../dist/${ASSET_NAME}" .
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
echo "Preparing GitHub release for ${VERSION} (prerelease=${PRERELEASE_FLAG}) with asset dist/${ASSET_NAME}"
|
||||||
|
|
||||||
|
if gh release view "${VERSION}" >/dev/null 2>&1; then
|
||||||
|
echo "Release ${VERSION} already exists. Uploading asset."
|
||||||
|
gh release upload "${VERSION}" "dist/${ASSET_NAME}" --clobber
|
||||||
|
else
|
||||||
|
ARGS=(
|
||||||
|
"${VERSION}"
|
||||||
|
"dist/${ASSET_NAME}"
|
||||||
|
--title
|
||||||
|
"Version ${VERSION}"
|
||||||
|
--notes
|
||||||
|
"Release generated from branch version/${VERSION}."
|
||||||
|
)
|
||||||
|
|
||||||
|
if [ "${PRERELEASE_FLAG}" = "true" ]; then
|
||||||
|
ARGS+=(--prerelease)
|
||||||
|
fi
|
||||||
|
|
||||||
|
gh release create "${ARGS[@]}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Optional delete version branch after merge (stable only)
|
||||||
|
if: needs.meta.outputs.is_prerelease == 'false'
|
||||||
|
env:
|
||||||
|
BRANCH: ${{ needs.meta.outputs.branch }}
|
||||||
|
run: |
|
||||||
|
echo "Deleting branch ${BRANCH} after squash merge and release"
|
||||||
|
git push origin --delete "${BRANCH}" || echo "Branch already deleted or cannot delete"
|
||||||
61
.github/workflows/updateserver.yml
vendored
Normal file
61
.github/workflows/updateserver.yml
vendored
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
- name: Update updates.xml (version, date, download URL only)
|
||||||
|
if: steps.meta.outputs.skip != 'true'
|
||||||
|
env:
|
||||||
|
VERSION: ${{ steps.meta.outputs.version }}
|
||||||
|
DOWNLOAD_URL: ${{ steps.meta.outputs.download_url }}
|
||||||
|
UPDATE_FILE: ${{ steps.parse.outputs.update_file }}
|
||||||
|
run: |
|
||||||
|
python << 'PY'
|
||||||
|
import os
|
||||||
|
from datetime import datetime
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
xml_path = Path(os.environ["UPDATE_FILE"])
|
||||||
|
if not xml_path.exists():
|
||||||
|
raise SystemExit(f"{xml_path} not found in update server repository")
|
||||||
|
|
||||||
|
tree = ET.parse(xml_path)
|
||||||
|
root = tree.getroot()
|
||||||
|
|
||||||
|
version = os.environ["VERSION"].strip()
|
||||||
|
download_url = os.environ["DOWNLOAD_URL"].strip()
|
||||||
|
today = datetime.utcnow().strftime("%Y-%m-%d")
|
||||||
|
|
||||||
|
# Find an existing update node for this version; if none exists, create one.
|
||||||
|
target = None
|
||||||
|
for upd in root.findall("update"):
|
||||||
|
v_node = upd.find("version")
|
||||||
|
if v_node is not None and (v_node.text or "").strip() == version:
|
||||||
|
target = upd
|
||||||
|
break
|
||||||
|
|
||||||
|
if target is None:
|
||||||
|
target = ET.SubElement(root, "update")
|
||||||
|
ET.SubElement(target, "version").text = version
|
||||||
|
|
||||||
|
# Ensure version is set (only set the version field)
|
||||||
|
v_node = target.find("version")
|
||||||
|
if v_node is None:
|
||||||
|
v_node = ET.SubElement(target, "version")
|
||||||
|
v_node.text = version
|
||||||
|
|
||||||
|
# Ensure creationDate is set
|
||||||
|
cd_node = target.find("creationDate")
|
||||||
|
if cd_node is None:
|
||||||
|
cd_node = ET.SubElement(target, "creationDate")
|
||||||
|
cd_node.text = today
|
||||||
|
|
||||||
|
# Ensure downloads/downloadurl exists; update text only, keep attributes as-is if present
|
||||||
|
downloads = target.find("downloads")
|
||||||
|
if downloads is None:
|
||||||
|
downloads = ET.SubElement(target, "downloads")
|
||||||
|
|
||||||
|
dl = downloads.find("downloadurl")
|
||||||
|
if dl is None:
|
||||||
|
dl = ET.SubElement(downloads, "downloadurl")
|
||||||
|
|
||||||
|
dl.text = download_url
|
||||||
|
|
||||||
|
tree.write(xml_path, encoding="utf-8", xml_declaration=True)
|
||||||
|
PY
|
||||||
227
.github/workflows/version_branch.yml
vendored
Normal file
227
.github/workflows/version_branch.yml
vendored
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
name: Create version branch and bump versions
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
new_version:
|
||||||
|
description: "New version (for example: 01.02.00). Leave blank to auto-increment from highest version/* branch."
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
base_branch:
|
||||||
|
description: "Base branch to create version branch from"
|
||||||
|
required: false
|
||||||
|
default: "main"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
create-version-branch:
|
||||||
|
name: Create version branch and update versions
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
env:
|
||||||
|
BASE_BRANCH: ${{ github.event.inputs.base_branch }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
ref: ${{ env.BASE_BRANCH }}
|
||||||
|
|
||||||
|
- name: Determine new version (input or auto-increment)
|
||||||
|
id: version
|
||||||
|
env:
|
||||||
|
INPUT_VERSION: ${{ github.event.inputs.new_version }}
|
||||||
|
run: |
|
||||||
|
python << 'PY'
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
input_version = os.environ.get("INPUT_VERSION", "").strip()
|
||||||
|
|
||||||
|
if input_version:
|
||||||
|
new_version = input_version
|
||||||
|
else:
|
||||||
|
completed = subprocess.run(
|
||||||
|
["git", "ls-remote", "--heads", "origin"],
|
||||||
|
check=True,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
pattern = re.compile(r"refs/heads/version/([0-9]+\.[0-9]+\.[0-9]+)$")
|
||||||
|
versions = []
|
||||||
|
|
||||||
|
for line in completed.stdout.splitlines():
|
||||||
|
parts = line.split()
|
||||||
|
if len(parts) != 2:
|
||||||
|
continue
|
||||||
|
ref = parts[1]
|
||||||
|
m = pattern.search(ref)
|
||||||
|
if not m:
|
||||||
|
continue
|
||||||
|
|
||||||
|
v_str = m.group(1)
|
||||||
|
try:
|
||||||
|
major, minor, patch = map(int, v_str.split("."))
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
versions.append((major, minor, patch))
|
||||||
|
|
||||||
|
if versions:
|
||||||
|
major, minor, patch = max(versions)
|
||||||
|
patch += 1
|
||||||
|
else:
|
||||||
|
major, minor, patch = 1, 0, 0
|
||||||
|
|
||||||
|
new_version = f"{major:02d}.{minor:02d}.{patch:02d}"
|
||||||
|
|
||||||
|
print(f"Using version {new_version}")
|
||||||
|
|
||||||
|
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as fh:
|
||||||
|
fh.write(f"new_version={new_version}\n")
|
||||||
|
PY
|
||||||
|
|
||||||
|
- name: Compute branch name
|
||||||
|
id: branch
|
||||||
|
env:
|
||||||
|
NEW_VERSION: ${{ steps.version.outputs.new_version }}
|
||||||
|
run: |
|
||||||
|
SAFE_VERSION="${NEW_VERSION// /-}"
|
||||||
|
BRANCH_NAME="version/${SAFE_VERSION}"
|
||||||
|
echo "Using branch name: $BRANCH_NAME"
|
||||||
|
echo "branch_name=$BRANCH_NAME" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Create version branch from base
|
||||||
|
run: |
|
||||||
|
git checkout -b "${{ steps.branch.outputs.branch_name }}"
|
||||||
|
|
||||||
|
- name: Bump version strings across repo
|
||||||
|
env:
|
||||||
|
NEW_VERSION: ${{ steps.version.outputs.new_version }}
|
||||||
|
run: |
|
||||||
|
echo "Updating version strings to ${NEW_VERSION} across repository"
|
||||||
|
|
||||||
|
python << 'PY'
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
new_version = os.environ["NEW_VERSION"]
|
||||||
|
|
||||||
|
targets = [
|
||||||
|
Path("src"),
|
||||||
|
Path("docs"),
|
||||||
|
]
|
||||||
|
|
||||||
|
patterns = [
|
||||||
|
(re.compile(r"\(VERSION\s+[0-9]+\.[0-9]+\.[0-9]+\)"), lambda v: f"(VERSION {v})"),
|
||||||
|
(re.compile(r"(VERSION[:\s]+)([0-9]+\.[0-9]+\.[0-9]+)"), lambda v: r"\1" + v),
|
||||||
|
(re.compile(r"(<version>)([0-9]+\.[0-9]+\.[0-9]+)(</version>)"), lambda v: r"\1" + v + r"\3"),
|
||||||
|
(re.compile(r'(<extension\\b[^>]*\\bversion=")([0-9]+\.[0-9]+\.[0-9]+)(")'), lambda v: r"\1" + v + r"\3"),
|
||||||
|
(re.compile(r'(\"version\"\s*:\s*\")(\d+\.\d+\.\d+)(\")'), lambda v: r"\1" + v + r"\3"),
|
||||||
|
]
|
||||||
|
|
||||||
|
def update_file(path: Path) -> bool:
|
||||||
|
try:
|
||||||
|
text = path.read_text(encoding="utf-8")
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
original = text
|
||||||
|
for regex, repl in patterns:
|
||||||
|
text = regex.sub(lambda m, r=repl: r(m), text)
|
||||||
|
|
||||||
|
if text != original:
|
||||||
|
path.write_text(text, encoding="utf-8")
|
||||||
|
print(f"Updated version in {path}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
for root in targets:
|
||||||
|
if not root.exists():
|
||||||
|
continue
|
||||||
|
|
||||||
|
for path in root.rglob("*"):
|
||||||
|
if not path.is_file():
|
||||||
|
continue
|
||||||
|
|
||||||
|
if path.suffix.lower() in {
|
||||||
|
".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico",
|
||||||
|
".zip", ".pdf", ".tar", ".gz",
|
||||||
|
}:
|
||||||
|
continue
|
||||||
|
|
||||||
|
update_file(path)
|
||||||
|
|
||||||
|
repo_root = Path(".").resolve()
|
||||||
|
|
||||||
|
def is_under_any_target(p: Path) -> bool:
|
||||||
|
for t in targets:
|
||||||
|
if not t.exists():
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
p.resolve().relative_to(t.resolve())
|
||||||
|
return True
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Explicitly update README.md and CHANGELOG.md
|
||||||
|
for fname in ["README.md", "CHANGELOG.md"]:
|
||||||
|
p = Path(fname)
|
||||||
|
if p.exists() and p.is_file():
|
||||||
|
update_file(p)
|
||||||
|
|
||||||
|
# Update remaining markdown files outside src/docs
|
||||||
|
for path in repo_root.rglob("*.md"):
|
||||||
|
if not path.is_file():
|
||||||
|
continue
|
||||||
|
|
||||||
|
if path.name.lower() in {"readme.md", "changelog.md"}:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if is_under_any_target(path):
|
||||||
|
continue
|
||||||
|
|
||||||
|
update_file(path)
|
||||||
|
PY
|
||||||
|
|
||||||
|
- name: Update CHANGELOG using script
|
||||||
|
env:
|
||||||
|
NEW_VERSION: ${{ steps.version.outputs.new_version }}
|
||||||
|
run: |
|
||||||
|
if [ ! -f "scripts/update_changelog.sh" ]; then
|
||||||
|
echo "scripts/update_changelog.sh not found. Failing version branch creation."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
chmod +x scripts/update_changelog.sh
|
||||||
|
./scripts/update_changelog.sh "${NEW_VERSION}"
|
||||||
|
|
||||||
|
- name: Configure git user
|
||||||
|
run: |
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
|
||||||
|
- name: Commit version bump
|
||||||
|
env:
|
||||||
|
NEW_VERSION: ${{ steps.version.outputs.new_version }}
|
||||||
|
run: |
|
||||||
|
git status
|
||||||
|
if git diff --quiet; then
|
||||||
|
echo "No changes detected after version bump. Skipping commit."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
git add -A
|
||||||
|
|
||||||
|
git commit -m "chore: bump version to ${NEW_VERSION}"
|
||||||
|
|
||||||
|
- name: Push version branch
|
||||||
|
run: |
|
||||||
|
git push -u origin "${{ steps.branch.outputs.branch_name }}"
|
||||||
@@ -15,8 +15,9 @@
|
|||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
DEFGROUP: Joomla.Template.Site
|
DEFGROUP: Joomla.Template.Site
|
||||||
INGROUP: Moko-Cassiopeia
|
INGROUP: Moko-Cassiopeia
|
||||||
|
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
|
||||||
PATH: ./templates/moko-cassiopeia/component.php
|
PATH: ./templates/moko-cassiopeia/component.php
|
||||||
VERSION: 03.00
|
VERSION: 03.00.00
|
||||||
BRIEF: Minimal component-only template file for Moko-Cassiopeia
|
BRIEF: Minimal component-only template file for Moko-Cassiopeia
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -15,8 +15,9 @@
|
|||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
DEFGROUP: Joomla.Template.Site
|
DEFGROUP: Joomla.Template.Site
|
||||||
INGROUP: Moko-Cassiopeia
|
INGROUP: Moko-Cassiopeia
|
||||||
|
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
|
||||||
PATH: ./templates/moko-cassiopeia/custom.php
|
PATH: ./templates/moko-cassiopeia/custom.php
|
||||||
VERSION: 03.00
|
VERSION: 03.00.00
|
||||||
BRIEF: Custom entry template file for Moko-Cassiopeia with user-defined overrides
|
BRIEF: Custom entry template file for Moko-Cassiopeia with user-defined overrides
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -15,8 +15,9 @@
|
|||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
DEFGROUP: Joomla.Template.Site
|
DEFGROUP: Joomla.Template.Site
|
||||||
INGROUP: Moko-Cassiopeia
|
INGROUP: Moko-Cassiopeia
|
||||||
|
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
|
||||||
PATH: ./templates/moko-cassiopeia/error.php
|
PATH: ./templates/moko-cassiopeia/error.php
|
||||||
VERSION: 03.00
|
VERSION: 03.00.00
|
||||||
BRIEF: Error page template file for Moko-Cassiopeia
|
BRIEF: Error page template file for Moko-Cassiopeia
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
BIN
src/templates/moko-cassiopeia/favicon.ico.bak
Normal file
BIN
src/templates/moko-cassiopeia/favicon.ico.bak
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -12,8 +12,8 @@
|
|||||||
You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/ .
|
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
|
# FILE INFORMATION
|
||||||
DEFGROUP: Joomla.Site
|
DEFGROUP: Joomla.Templates.Site
|
||||||
INGROUP: Templates.Moko-Cassiopeia
|
INGROUP: Moko-Cassiopeia
|
||||||
FILE: index.html
|
FILE: index.html
|
||||||
BRIEF: Security redirect page to block folder access and forward to site root.
|
BRIEF: Security redirect page to block folder access and forward to site root.
|
||||||
-->
|
-->
|
||||||
@@ -12,8 +12,8 @@
|
|||||||
You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/ .
|
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
|
# FILE INFORMATION
|
||||||
DEFGROUP: Joomla.Site
|
DEFGROUP: Joomla.Templates.Site
|
||||||
INGROUP: Templates.Moko-Cassiopeia
|
INGROUP: Moko-Cassiopeia
|
||||||
FILE: index.html
|
FILE: index.html
|
||||||
BRIEF: Security redirect page to block folder access and forward to site root.
|
BRIEF: Security redirect page to block folder access and forward to site root.
|
||||||
-->
|
-->
|
||||||
@@ -12,8 +12,8 @@
|
|||||||
You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/ .
|
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
|
# FILE INFORMATION
|
||||||
DEFGROUP: Joomla.Site
|
DEFGROUP: Joomla.Templates.Site
|
||||||
INGROUP: Templates.Moko-Cassiopeia
|
INGROUP: Moko-Cassiopeia
|
||||||
FILE: index.html
|
FILE: index.html
|
||||||
BRIEF: Security redirect page to block folder access and forward to site root.
|
BRIEF: Security redirect page to block folder access and forward to site root.
|
||||||
-->
|
-->
|
||||||
@@ -15,8 +15,9 @@
|
|||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
DEFGROUP: Joomla.Template.Site
|
DEFGROUP: Joomla.Template.Site
|
||||||
INGROUP: Moko-Cassiopeia
|
INGROUP: Moko-Cassiopeia
|
||||||
|
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
|
||||||
PATH: ./templates/moko-cassiopeia/html/com_content/article/toc-left.php
|
PATH: ./templates/moko-cassiopeia/html/com_content/article/toc-left.php
|
||||||
VERSION: 03.00
|
VERSION: 03.00.00
|
||||||
BRIEF: Template override for Joomla articles with Table of Contents aligned left
|
BRIEF: Template override for Joomla articles with Table of Contents aligned left
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -15,8 +15,9 @@
|
|||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
DEFGROUP: Joomla.Template.Site
|
DEFGROUP: Joomla.Template.Site
|
||||||
INGROUP: Moko-Cassiopeia
|
INGROUP: Moko-Cassiopeia
|
||||||
|
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
|
||||||
PATH: ./templates/moko-cassiopeia/html/com_content/article/toc-right.php
|
PATH: ./templates/moko-cassiopeia/html/com_content/article/toc-right.php
|
||||||
VERSION: 03.00
|
VERSION: 03.00.00
|
||||||
BRIEF: Template override for Joomla articles with Table of Contents aligned right
|
BRIEF: Template override for Joomla articles with Table of Contents aligned right
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -12,8 +12,8 @@
|
|||||||
You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/ .
|
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
|
# FILE INFORMATION
|
||||||
DEFGROUP: Joomla.Site
|
DEFGROUP: Joomla.Templates.Site
|
||||||
INGROUP: Templates.Moko-Cassiopeia
|
INGROUP: Moko-Cassiopeia
|
||||||
FILE: index.html
|
FILE: index.html
|
||||||
BRIEF: Security redirect page to block folder access and forward to site root.
|
BRIEF: Security redirect page to block folder access and forward to site root.
|
||||||
-->
|
-->
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
|
||||||
|
|
||||||
|
This file is part of a Moko Consulting project.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
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.Templates.Site
|
||||||
|
INGROUP: Moko-Cassiopeia
|
||||||
|
FILE: index.html
|
||||||
|
BRIEF: Security redirect page to block folder access and forward to site root.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Redirecting…</title>
|
||||||
|
|
||||||
|
<!-- Search engines: do not index this placeholder redirect page -->
|
||||||
|
<meta name="robots" content="noindex, nofollow, noarchive" />
|
||||||
|
|
||||||
|
<!-- Instant redirect fallback even if JavaScript is disabled -->
|
||||||
|
<meta http-equiv="refresh" content="0; url=/" />
|
||||||
|
|
||||||
|
<!-- Canonical root reference -->
|
||||||
|
<link rel="canonical" href="/" />
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
(function redirectToRoot() {
|
||||||
|
// Configuration object with safe defaults.
|
||||||
|
var opts = {
|
||||||
|
fallbackPath: "/", // string: fallback destination if origin is unavailable
|
||||||
|
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
|
||||||
|
behavior: "replace" // enum: "replace" | "assign"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determine absolute origin in all mainstream browsers.
|
||||||
|
var origin = (typeof location.origin === "string" && location.origin)
|
||||||
|
|| (location.protocol + "//" + location.host);
|
||||||
|
|
||||||
|
// Final destination: absolute root of the current site, or fallback path.
|
||||||
|
var destination = origin ? origin + "/" : opts.fallbackPath;
|
||||||
|
|
||||||
|
function go() {
|
||||||
|
if (opts.behavior === "assign") {
|
||||||
|
location.assign(destination);
|
||||||
|
} else {
|
||||||
|
location.replace(destination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute redirect, optionally after a short delay.
|
||||||
|
if (opts.delayMs > 0) {
|
||||||
|
setTimeout(go, opts.delayMs);
|
||||||
|
} else {
|
||||||
|
go();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Secondary meta-refresh for no-JS environments is already set above.
|
||||||
|
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<noscript>
|
||||||
|
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
|
||||||
|
<style>
|
||||||
|
html, body { height:100%; }
|
||||||
|
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
|
||||||
|
.msg { opacity: .75; text-align: center; }
|
||||||
|
</style>
|
||||||
|
</noscript>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
|
||||||
|
|
||||||
|
This file is part of a Moko Consulting project.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
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.Templates.Site
|
||||||
|
INGROUP: Moko-Cassiopeia
|
||||||
|
FILE: index.html
|
||||||
|
BRIEF: Security redirect page to block folder access and forward to site root.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Redirecting…</title>
|
||||||
|
|
||||||
|
<!-- Search engines: do not index this placeholder redirect page -->
|
||||||
|
<meta name="robots" content="noindex, nofollow, noarchive" />
|
||||||
|
|
||||||
|
<!-- Instant redirect fallback even if JavaScript is disabled -->
|
||||||
|
<meta http-equiv="refresh" content="0; url=/" />
|
||||||
|
|
||||||
|
<!-- Canonical root reference -->
|
||||||
|
<link rel="canonical" href="/" />
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
(function redirectToRoot() {
|
||||||
|
// Configuration object with safe defaults.
|
||||||
|
var opts = {
|
||||||
|
fallbackPath: "/", // string: fallback destination if origin is unavailable
|
||||||
|
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
|
||||||
|
behavior: "replace" // enum: "replace" | "assign"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determine absolute origin in all mainstream browsers.
|
||||||
|
var origin = (typeof location.origin === "string" && location.origin)
|
||||||
|
|| (location.protocol + "//" + location.host);
|
||||||
|
|
||||||
|
// Final destination: absolute root of the current site, or fallback path.
|
||||||
|
var destination = origin ? origin + "/" : opts.fallbackPath;
|
||||||
|
|
||||||
|
function go() {
|
||||||
|
if (opts.behavior === "assign") {
|
||||||
|
location.assign(destination);
|
||||||
|
} else {
|
||||||
|
location.replace(destination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute redirect, optionally after a short delay.
|
||||||
|
if (opts.delayMs > 0) {
|
||||||
|
setTimeout(go, opts.delayMs);
|
||||||
|
} else {
|
||||||
|
go();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Secondary meta-refresh for no-JS environments is already set above.
|
||||||
|
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<noscript>
|
||||||
|
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
|
||||||
|
<style>
|
||||||
|
html, body { height:100%; }
|
||||||
|
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
|
||||||
|
.msg { opacity: .75; text-align: center; }
|
||||||
|
</style>
|
||||||
|
</noscript>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
89
src/templates/moko-cassiopeia/html/com_content/index.html
Normal file
89
src/templates/moko-cassiopeia/html/com_content/index.html
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
|
||||||
|
|
||||||
|
This file is part of a Moko Consulting project.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
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.Templates.Site
|
||||||
|
INGROUP: Moko-Cassiopeia
|
||||||
|
FILE: index.html
|
||||||
|
BRIEF: Security redirect page to block folder access and forward to site root.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Redirecting…</title>
|
||||||
|
|
||||||
|
<!-- Search engines: do not index this placeholder redirect page -->
|
||||||
|
<meta name="robots" content="noindex, nofollow, noarchive" />
|
||||||
|
|
||||||
|
<!-- Instant redirect fallback even if JavaScript is disabled -->
|
||||||
|
<meta http-equiv="refresh" content="0; url=/" />
|
||||||
|
|
||||||
|
<!-- Canonical root reference -->
|
||||||
|
<link rel="canonical" href="/" />
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
(function redirectToRoot() {
|
||||||
|
// Configuration object with safe defaults.
|
||||||
|
var opts = {
|
||||||
|
fallbackPath: "/", // string: fallback destination if origin is unavailable
|
||||||
|
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
|
||||||
|
behavior: "replace" // enum: "replace" | "assign"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determine absolute origin in all mainstream browsers.
|
||||||
|
var origin = (typeof location.origin === "string" && location.origin)
|
||||||
|
|| (location.protocol + "//" + location.host);
|
||||||
|
|
||||||
|
// Final destination: absolute root of the current site, or fallback path.
|
||||||
|
var destination = origin ? origin + "/" : opts.fallbackPath;
|
||||||
|
|
||||||
|
function go() {
|
||||||
|
if (opts.behavior === "assign") {
|
||||||
|
location.assign(destination);
|
||||||
|
} else {
|
||||||
|
location.replace(destination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute redirect, optionally after a short delay.
|
||||||
|
if (opts.delayMs > 0) {
|
||||||
|
setTimeout(go, opts.delayMs);
|
||||||
|
} else {
|
||||||
|
go();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Secondary meta-refresh for no-JS environments is already set above.
|
||||||
|
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<noscript>
|
||||||
|
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
|
||||||
|
<style>
|
||||||
|
html, body { height:100%; }
|
||||||
|
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
|
||||||
|
.msg { opacity: .75; text-align: center; }
|
||||||
|
</style>
|
||||||
|
</noscript>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
|
||||||
|
|
||||||
|
This file is part of a Moko Consulting project.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
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.Templates.Site
|
||||||
|
INGROUP: Moko-Cassiopeia
|
||||||
|
FILE: index.html
|
||||||
|
BRIEF: Security redirect page to block folder access and forward to site root.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Redirecting…</title>
|
||||||
|
|
||||||
|
<!-- Search engines: do not index this placeholder redirect page -->
|
||||||
|
<meta name="robots" content="noindex, nofollow, noarchive" />
|
||||||
|
|
||||||
|
<!-- Instant redirect fallback even if JavaScript is disabled -->
|
||||||
|
<meta http-equiv="refresh" content="0; url=/" />
|
||||||
|
|
||||||
|
<!-- Canonical root reference -->
|
||||||
|
<link rel="canonical" href="/" />
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
(function redirectToRoot() {
|
||||||
|
// Configuration object with safe defaults.
|
||||||
|
var opts = {
|
||||||
|
fallbackPath: "/", // string: fallback destination if origin is unavailable
|
||||||
|
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
|
||||||
|
behavior: "replace" // enum: "replace" | "assign"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determine absolute origin in all mainstream browsers.
|
||||||
|
var origin = (typeof location.origin === "string" && location.origin)
|
||||||
|
|| (location.protocol + "//" + location.host);
|
||||||
|
|
||||||
|
// Final destination: absolute root of the current site, or fallback path.
|
||||||
|
var destination = origin ? origin + "/" : opts.fallbackPath;
|
||||||
|
|
||||||
|
function go() {
|
||||||
|
if (opts.behavior === "assign") {
|
||||||
|
location.assign(destination);
|
||||||
|
} else {
|
||||||
|
location.replace(destination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute redirect, optionally after a short delay.
|
||||||
|
if (opts.delayMs > 0) {
|
||||||
|
setTimeout(go, opts.delayMs);
|
||||||
|
} else {
|
||||||
|
go();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Secondary meta-refresh for no-JS environments is already set above.
|
||||||
|
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<noscript>
|
||||||
|
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
|
||||||
|
<style>
|
||||||
|
html, body { height:100%; }
|
||||||
|
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
|
||||||
|
.msg { opacity: .75; text-align: center; }
|
||||||
|
</style>
|
||||||
|
</noscript>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
89
src/templates/moko-cassiopeia/html/com_engage/index.html
Normal file
89
src/templates/moko-cassiopeia/html/com_engage/index.html
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
|
||||||
|
|
||||||
|
This file is part of a Moko Consulting project.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
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.Templates.Site
|
||||||
|
INGROUP: Moko-Cassiopeia
|
||||||
|
FILE: index.html
|
||||||
|
BRIEF: Security redirect page to block folder access and forward to site root.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Redirecting…</title>
|
||||||
|
|
||||||
|
<!-- Search engines: do not index this placeholder redirect page -->
|
||||||
|
<meta name="robots" content="noindex, nofollow, noarchive" />
|
||||||
|
|
||||||
|
<!-- Instant redirect fallback even if JavaScript is disabled -->
|
||||||
|
<meta http-equiv="refresh" content="0; url=/" />
|
||||||
|
|
||||||
|
<!-- Canonical root reference -->
|
||||||
|
<link rel="canonical" href="/" />
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
(function redirectToRoot() {
|
||||||
|
// Configuration object with safe defaults.
|
||||||
|
var opts = {
|
||||||
|
fallbackPath: "/", // string: fallback destination if origin is unavailable
|
||||||
|
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
|
||||||
|
behavior: "replace" // enum: "replace" | "assign"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determine absolute origin in all mainstream browsers.
|
||||||
|
var origin = (typeof location.origin === "string" && location.origin)
|
||||||
|
|| (location.protocol + "//" + location.host);
|
||||||
|
|
||||||
|
// Final destination: absolute root of the current site, or fallback path.
|
||||||
|
var destination = origin ? origin + "/" : opts.fallbackPath;
|
||||||
|
|
||||||
|
function go() {
|
||||||
|
if (opts.behavior === "assign") {
|
||||||
|
location.assign(destination);
|
||||||
|
} else {
|
||||||
|
location.replace(destination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute redirect, optionally after a short delay.
|
||||||
|
if (opts.delayMs > 0) {
|
||||||
|
setTimeout(go, opts.delayMs);
|
||||||
|
} else {
|
||||||
|
go();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Secondary meta-refresh for no-JS environments is already set above.
|
||||||
|
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<noscript>
|
||||||
|
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
|
||||||
|
<style>
|
||||||
|
html, body { height:100%; }
|
||||||
|
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
|
||||||
|
.msg { opacity: .75; text-align: center; }
|
||||||
|
</style>
|
||||||
|
</noscript>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
|
||||||
|
|
||||||
|
This file is part of a Moko Consulting project.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
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.Templates.Site
|
||||||
|
INGROUP: Moko-Cassiopeia
|
||||||
|
FILE: index.html
|
||||||
|
BRIEF: Security redirect page to block folder access and forward to site root.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Redirecting…</title>
|
||||||
|
|
||||||
|
<!-- Search engines: do not index this placeholder redirect page -->
|
||||||
|
<meta name="robots" content="noindex, nofollow, noarchive" />
|
||||||
|
|
||||||
|
<!-- Instant redirect fallback even if JavaScript is disabled -->
|
||||||
|
<meta http-equiv="refresh" content="0; url=/" />
|
||||||
|
|
||||||
|
<!-- Canonical root reference -->
|
||||||
|
<link rel="canonical" href="/" />
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
(function redirectToRoot() {
|
||||||
|
// Configuration object with safe defaults.
|
||||||
|
var opts = {
|
||||||
|
fallbackPath: "/", // string: fallback destination if origin is unavailable
|
||||||
|
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
|
||||||
|
behavior: "replace" // enum: "replace" | "assign"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determine absolute origin in all mainstream browsers.
|
||||||
|
var origin = (typeof location.origin === "string" && location.origin)
|
||||||
|
|| (location.protocol + "//" + location.host);
|
||||||
|
|
||||||
|
// Final destination: absolute root of the current site, or fallback path.
|
||||||
|
var destination = origin ? origin + "/" : opts.fallbackPath;
|
||||||
|
|
||||||
|
function go() {
|
||||||
|
if (opts.behavior === "assign") {
|
||||||
|
location.assign(destination);
|
||||||
|
} else {
|
||||||
|
location.replace(destination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute redirect, optionally after a short delay.
|
||||||
|
if (opts.delayMs > 0) {
|
||||||
|
setTimeout(go, opts.delayMs);
|
||||||
|
} else {
|
||||||
|
go();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Secondary meta-refresh for no-JS environments is already set above.
|
||||||
|
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<noscript>
|
||||||
|
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
|
||||||
|
<style>
|
||||||
|
html, body { height:100%; }
|
||||||
|
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
|
||||||
|
.msg { opacity: .75; text-align: center; }
|
||||||
|
</style>
|
||||||
|
</noscript>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
|
||||||
|
|
||||||
|
This file is part of a Moko Consulting project.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
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.Templates.Site
|
||||||
|
INGROUP: Moko-Cassiopeia
|
||||||
|
FILE: index.html
|
||||||
|
BRIEF: Security redirect page to block folder access and forward to site root.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Redirecting…</title>
|
||||||
|
|
||||||
|
<!-- Search engines: do not index this placeholder redirect page -->
|
||||||
|
<meta name="robots" content="noindex, nofollow, noarchive" />
|
||||||
|
|
||||||
|
<!-- Instant redirect fallback even if JavaScript is disabled -->
|
||||||
|
<meta http-equiv="refresh" content="0; url=/" />
|
||||||
|
|
||||||
|
<!-- Canonical root reference -->
|
||||||
|
<link rel="canonical" href="/" />
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
(function redirectToRoot() {
|
||||||
|
// Configuration object with safe defaults.
|
||||||
|
var opts = {
|
||||||
|
fallbackPath: "/", // string: fallback destination if origin is unavailable
|
||||||
|
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
|
||||||
|
behavior: "replace" // enum: "replace" | "assign"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determine absolute origin in all mainstream browsers.
|
||||||
|
var origin = (typeof location.origin === "string" && location.origin)
|
||||||
|
|| (location.protocol + "//" + location.host);
|
||||||
|
|
||||||
|
// Final destination: absolute root of the current site, or fallback path.
|
||||||
|
var destination = origin ? origin + "/" : opts.fallbackPath;
|
||||||
|
|
||||||
|
function go() {
|
||||||
|
if (opts.behavior === "assign") {
|
||||||
|
location.assign(destination);
|
||||||
|
} else {
|
||||||
|
location.replace(destination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute redirect, optionally after a short delay.
|
||||||
|
if (opts.delayMs > 0) {
|
||||||
|
setTimeout(go, opts.delayMs);
|
||||||
|
} else {
|
||||||
|
go();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Secondary meta-refresh for no-JS environments is already set above.
|
||||||
|
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<noscript>
|
||||||
|
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
|
||||||
|
<style>
|
||||||
|
html, body { height:100%; }
|
||||||
|
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
|
||||||
|
.msg { opacity: .75; text-align: center; }
|
||||||
|
</style>
|
||||||
|
</noscript>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
|
||||||
|
|
||||||
|
This file is part of a Moko Consulting project.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
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.Templates.Site
|
||||||
|
INGROUP: Moko-Cassiopeia
|
||||||
|
FILE: index.html
|
||||||
|
BRIEF: Security redirect page to block folder access and forward to site root.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Redirecting…</title>
|
||||||
|
|
||||||
|
<!-- Search engines: do not index this placeholder redirect page -->
|
||||||
|
<meta name="robots" content="noindex, nofollow, noarchive" />
|
||||||
|
|
||||||
|
<!-- Instant redirect fallback even if JavaScript is disabled -->
|
||||||
|
<meta http-equiv="refresh" content="0; url=/" />
|
||||||
|
|
||||||
|
<!-- Canonical root reference -->
|
||||||
|
<link rel="canonical" href="/" />
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
(function redirectToRoot() {
|
||||||
|
// Configuration object with safe defaults.
|
||||||
|
var opts = {
|
||||||
|
fallbackPath: "/", // string: fallback destination if origin is unavailable
|
||||||
|
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
|
||||||
|
behavior: "replace" // enum: "replace" | "assign"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determine absolute origin in all mainstream browsers.
|
||||||
|
var origin = (typeof location.origin === "string" && location.origin)
|
||||||
|
|| (location.protocol + "//" + location.host);
|
||||||
|
|
||||||
|
// Final destination: absolute root of the current site, or fallback path.
|
||||||
|
var destination = origin ? origin + "/" : opts.fallbackPath;
|
||||||
|
|
||||||
|
function go() {
|
||||||
|
if (opts.behavior === "assign") {
|
||||||
|
location.assign(destination);
|
||||||
|
} else {
|
||||||
|
location.replace(destination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute redirect, optionally after a short delay.
|
||||||
|
if (opts.delayMs > 0) {
|
||||||
|
setTimeout(go, opts.delayMs);
|
||||||
|
} else {
|
||||||
|
go();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Secondary meta-refresh for no-JS environments is already set above.
|
||||||
|
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<noscript>
|
||||||
|
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
|
||||||
|
<style>
|
||||||
|
html, body { height:100%; }
|
||||||
|
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
|
||||||
|
.msg { opacity: .75; text-align: center; }
|
||||||
|
</style>
|
||||||
|
</noscript>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
|
||||||
|
|
||||||
|
This file is part of a Moko Consulting project.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
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.Templates.Site
|
||||||
|
INGROUP: Moko-Cassiopeia
|
||||||
|
FILE: index.html
|
||||||
|
BRIEF: Security redirect page to block folder access and forward to site root.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Redirecting…</title>
|
||||||
|
|
||||||
|
<!-- Search engines: do not index this placeholder redirect page -->
|
||||||
|
<meta name="robots" content="noindex, nofollow, noarchive" />
|
||||||
|
|
||||||
|
<!-- Instant redirect fallback even if JavaScript is disabled -->
|
||||||
|
<meta http-equiv="refresh" content="0; url=/" />
|
||||||
|
|
||||||
|
<!-- Canonical root reference -->
|
||||||
|
<link rel="canonical" href="/" />
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
(function redirectToRoot() {
|
||||||
|
// Configuration object with safe defaults.
|
||||||
|
var opts = {
|
||||||
|
fallbackPath: "/", // string: fallback destination if origin is unavailable
|
||||||
|
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
|
||||||
|
behavior: "replace" // enum: "replace" | "assign"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determine absolute origin in all mainstream browsers.
|
||||||
|
var origin = (typeof location.origin === "string" && location.origin)
|
||||||
|
|| (location.protocol + "//" + location.host);
|
||||||
|
|
||||||
|
// Final destination: absolute root of the current site, or fallback path.
|
||||||
|
var destination = origin ? origin + "/" : opts.fallbackPath;
|
||||||
|
|
||||||
|
function go() {
|
||||||
|
if (opts.behavior === "assign") {
|
||||||
|
location.assign(destination);
|
||||||
|
} else {
|
||||||
|
location.replace(destination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute redirect, optionally after a short delay.
|
||||||
|
if (opts.delayMs > 0) {
|
||||||
|
setTimeout(go, opts.delayMs);
|
||||||
|
} else {
|
||||||
|
go();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Secondary meta-refresh for no-JS environments is already set above.
|
||||||
|
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<noscript>
|
||||||
|
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
|
||||||
|
<style>
|
||||||
|
html, body { height:100%; }
|
||||||
|
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
|
||||||
|
.msg { opacity: .75; text-align: center; }
|
||||||
|
</style>
|
||||||
|
</noscript>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
|
||||||
|
|
||||||
|
This file is part of a Moko Consulting project.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
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.Templates.Site
|
||||||
|
INGROUP: Moko-Cassiopeia
|
||||||
|
FILE: index.html
|
||||||
|
BRIEF: Security redirect page to block folder access and forward to site root.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Redirecting…</title>
|
||||||
|
|
||||||
|
<!-- Search engines: do not index this placeholder redirect page -->
|
||||||
|
<meta name="robots" content="noindex, nofollow, noarchive" />
|
||||||
|
|
||||||
|
<!-- Instant redirect fallback even if JavaScript is disabled -->
|
||||||
|
<meta http-equiv="refresh" content="0; url=/" />
|
||||||
|
|
||||||
|
<!-- Canonical root reference -->
|
||||||
|
<link rel="canonical" href="/" />
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
(function redirectToRoot() {
|
||||||
|
// Configuration object with safe defaults.
|
||||||
|
var opts = {
|
||||||
|
fallbackPath: "/", // string: fallback destination if origin is unavailable
|
||||||
|
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
|
||||||
|
behavior: "replace" // enum: "replace" | "assign"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determine absolute origin in all mainstream browsers.
|
||||||
|
var origin = (typeof location.origin === "string" && location.origin)
|
||||||
|
|| (location.protocol + "//" + location.host);
|
||||||
|
|
||||||
|
// Final destination: absolute root of the current site, or fallback path.
|
||||||
|
var destination = origin ? origin + "/" : opts.fallbackPath;
|
||||||
|
|
||||||
|
function go() {
|
||||||
|
if (opts.behavior === "assign") {
|
||||||
|
location.assign(destination);
|
||||||
|
} else {
|
||||||
|
location.replace(destination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute redirect, optionally after a short delay.
|
||||||
|
if (opts.delayMs > 0) {
|
||||||
|
setTimeout(go, opts.delayMs);
|
||||||
|
} else {
|
||||||
|
go();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Secondary meta-refresh for no-JS environments is already set above.
|
||||||
|
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<noscript>
|
||||||
|
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
|
||||||
|
<style>
|
||||||
|
html, body { height:100%; }
|
||||||
|
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
|
||||||
|
.msg { opacity: .75; text-align: center; }
|
||||||
|
</style>
|
||||||
|
</noscript>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
|
||||||
|
|
||||||
|
This file is part of a Moko Consulting project.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
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.Templates.Site
|
||||||
|
INGROUP: Moko-Cassiopeia
|
||||||
|
FILE: index.html
|
||||||
|
BRIEF: Security redirect page to block folder access and forward to site root.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Redirecting…</title>
|
||||||
|
|
||||||
|
<!-- Search engines: do not index this placeholder redirect page -->
|
||||||
|
<meta name="robots" content="noindex, nofollow, noarchive" />
|
||||||
|
|
||||||
|
<!-- Instant redirect fallback even if JavaScript is disabled -->
|
||||||
|
<meta http-equiv="refresh" content="0; url=/" />
|
||||||
|
|
||||||
|
<!-- Canonical root reference -->
|
||||||
|
<link rel="canonical" href="/" />
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
(function redirectToRoot() {
|
||||||
|
// Configuration object with safe defaults.
|
||||||
|
var opts = {
|
||||||
|
fallbackPath: "/", // string: fallback destination if origin is unavailable
|
||||||
|
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
|
||||||
|
behavior: "replace" // enum: "replace" | "assign"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determine absolute origin in all mainstream browsers.
|
||||||
|
var origin = (typeof location.origin === "string" && location.origin)
|
||||||
|
|| (location.protocol + "//" + location.host);
|
||||||
|
|
||||||
|
// Final destination: absolute root of the current site, or fallback path.
|
||||||
|
var destination = origin ? origin + "/" : opts.fallbackPath;
|
||||||
|
|
||||||
|
function go() {
|
||||||
|
if (opts.behavior === "assign") {
|
||||||
|
location.assign(destination);
|
||||||
|
} else {
|
||||||
|
location.replace(destination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute redirect, optionally after a short delay.
|
||||||
|
if (opts.delayMs > 0) {
|
||||||
|
setTimeout(go, opts.delayMs);
|
||||||
|
} else {
|
||||||
|
go();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Secondary meta-refresh for no-JS environments is already set above.
|
||||||
|
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<noscript>
|
||||||
|
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
|
||||||
|
<style>
|
||||||
|
html, body { height:100%; }
|
||||||
|
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
|
||||||
|
.msg { opacity: .75; text-align: center; }
|
||||||
|
</style>
|
||||||
|
</noscript>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
89
src/templates/moko-cassiopeia/html/index.html
Normal file
89
src/templates/moko-cassiopeia/html/index.html
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
|
||||||
|
|
||||||
|
This file is part of a Moko Consulting project.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
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.Templates.Site
|
||||||
|
INGROUP: Moko-Cassiopeia
|
||||||
|
FILE: index.html
|
||||||
|
BRIEF: Security redirect page to block folder access and forward to site root.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Redirecting…</title>
|
||||||
|
|
||||||
|
<!-- Search engines: do not index this placeholder redirect page -->
|
||||||
|
<meta name="robots" content="noindex, nofollow, noarchive" />
|
||||||
|
|
||||||
|
<!-- Instant redirect fallback even if JavaScript is disabled -->
|
||||||
|
<meta http-equiv="refresh" content="0; url=/" />
|
||||||
|
|
||||||
|
<!-- Canonical root reference -->
|
||||||
|
<link rel="canonical" href="/" />
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
(function redirectToRoot() {
|
||||||
|
// Configuration object with safe defaults.
|
||||||
|
var opts = {
|
||||||
|
fallbackPath: "/", // string: fallback destination if origin is unavailable
|
||||||
|
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
|
||||||
|
behavior: "replace" // enum: "replace" | "assign"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determine absolute origin in all mainstream browsers.
|
||||||
|
var origin = (typeof location.origin === "string" && location.origin)
|
||||||
|
|| (location.protocol + "//" + location.host);
|
||||||
|
|
||||||
|
// Final destination: absolute root of the current site, or fallback path.
|
||||||
|
var destination = origin ? origin + "/" : opts.fallbackPath;
|
||||||
|
|
||||||
|
function go() {
|
||||||
|
if (opts.behavior === "assign") {
|
||||||
|
location.assign(destination);
|
||||||
|
} else {
|
||||||
|
location.replace(destination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute redirect, optionally after a short delay.
|
||||||
|
if (opts.delayMs > 0) {
|
||||||
|
setTimeout(go, opts.delayMs);
|
||||||
|
} else {
|
||||||
|
go();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Secondary meta-refresh for no-JS environments is already set above.
|
||||||
|
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<noscript>
|
||||||
|
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
|
||||||
|
<style>
|
||||||
|
html, body { height:100%; }
|
||||||
|
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
|
||||||
|
.msg { opacity: .75; text-align: center; }
|
||||||
|
</style>
|
||||||
|
</noscript>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
|
||||||
|
|
||||||
|
This file is part of a Moko Consulting project.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
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.Templates.Site
|
||||||
|
INGROUP: Moko-Cassiopeia
|
||||||
|
FILE: index.html
|
||||||
|
BRIEF: Security redirect page to block folder access and forward to site root.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Redirecting…</title>
|
||||||
|
|
||||||
|
<!-- Search engines: do not index this placeholder redirect page -->
|
||||||
|
<meta name="robots" content="noindex, nofollow, noarchive" />
|
||||||
|
|
||||||
|
<!-- Instant redirect fallback even if JavaScript is disabled -->
|
||||||
|
<meta http-equiv="refresh" content="0; url=/" />
|
||||||
|
|
||||||
|
<!-- Canonical root reference -->
|
||||||
|
<link rel="canonical" href="/" />
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
(function redirectToRoot() {
|
||||||
|
// Configuration object with safe defaults.
|
||||||
|
var opts = {
|
||||||
|
fallbackPath: "/", // string: fallback destination if origin is unavailable
|
||||||
|
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
|
||||||
|
behavior: "replace" // enum: "replace" | "assign"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determine absolute origin in all mainstream browsers.
|
||||||
|
var origin = (typeof location.origin === "string" && location.origin)
|
||||||
|
|| (location.protocol + "//" + location.host);
|
||||||
|
|
||||||
|
// Final destination: absolute root of the current site, or fallback path.
|
||||||
|
var destination = origin ? origin + "/" : opts.fallbackPath;
|
||||||
|
|
||||||
|
function go() {
|
||||||
|
if (opts.behavior === "assign") {
|
||||||
|
location.assign(destination);
|
||||||
|
} else {
|
||||||
|
location.replace(destination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute redirect, optionally after a short delay.
|
||||||
|
if (opts.delayMs > 0) {
|
||||||
|
setTimeout(go, opts.delayMs);
|
||||||
|
} else {
|
||||||
|
go();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Secondary meta-refresh for no-JS environments is already set above.
|
||||||
|
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<noscript>
|
||||||
|
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
|
||||||
|
<style>
|
||||||
|
html, body { height:100%; }
|
||||||
|
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
|
||||||
|
.msg { opacity: .75; text-align: center; }
|
||||||
|
</style>
|
||||||
|
</noscript>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
89
src/templates/moko-cassiopeia/html/layouts/index.html
Normal file
89
src/templates/moko-cassiopeia/html/layouts/index.html
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
|
||||||
|
|
||||||
|
This file is part of a Moko Consulting project.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
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.Templates.Site
|
||||||
|
INGROUP: Moko-Cassiopeia
|
||||||
|
FILE: index.html
|
||||||
|
BRIEF: Security redirect page to block folder access and forward to site root.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Redirecting…</title>
|
||||||
|
|
||||||
|
<!-- Search engines: do not index this placeholder redirect page -->
|
||||||
|
<meta name="robots" content="noindex, nofollow, noarchive" />
|
||||||
|
|
||||||
|
<!-- Instant redirect fallback even if JavaScript is disabled -->
|
||||||
|
<meta http-equiv="refresh" content="0; url=/" />
|
||||||
|
|
||||||
|
<!-- Canonical root reference -->
|
||||||
|
<link rel="canonical" href="/" />
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
(function redirectToRoot() {
|
||||||
|
// Configuration object with safe defaults.
|
||||||
|
var opts = {
|
||||||
|
fallbackPath: "/", // string: fallback destination if origin is unavailable
|
||||||
|
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
|
||||||
|
behavior: "replace" // enum: "replace" | "assign"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determine absolute origin in all mainstream browsers.
|
||||||
|
var origin = (typeof location.origin === "string" && location.origin)
|
||||||
|
|| (location.protocol + "//" + location.host);
|
||||||
|
|
||||||
|
// Final destination: absolute root of the current site, or fallback path.
|
||||||
|
var destination = origin ? origin + "/" : opts.fallbackPath;
|
||||||
|
|
||||||
|
function go() {
|
||||||
|
if (opts.behavior === "assign") {
|
||||||
|
location.assign(destination);
|
||||||
|
} else {
|
||||||
|
location.replace(destination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute redirect, optionally after a short delay.
|
||||||
|
if (opts.delayMs > 0) {
|
||||||
|
setTimeout(go, opts.delayMs);
|
||||||
|
} else {
|
||||||
|
go();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Secondary meta-refresh for no-JS environments is already set above.
|
||||||
|
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<noscript>
|
||||||
|
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
|
||||||
|
<style>
|
||||||
|
html, body { height:100%; }
|
||||||
|
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
|
||||||
|
.msg { opacity: .75; text-align: center; }
|
||||||
|
</style>
|
||||||
|
</noscript>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
89
src/templates/moko-cassiopeia/html/mod_custom/index.html
Normal file
89
src/templates/moko-cassiopeia/html/mod_custom/index.html
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
<!-- Copyright (C) 2025 Moko Consulting <jmiller@mokoconsulting.tech>
|
||||||
|
|
||||||
|
This file is part of a Moko Consulting project.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
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.Templates.Site
|
||||||
|
INGROUP: Moko-Cassiopeia
|
||||||
|
FILE: index.html
|
||||||
|
BRIEF: Security redirect page to block folder access and forward to site root.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Redirecting…</title>
|
||||||
|
|
||||||
|
<!-- Search engines: do not index this placeholder redirect page -->
|
||||||
|
<meta name="robots" content="noindex, nofollow, noarchive" />
|
||||||
|
|
||||||
|
<!-- Instant redirect fallback even if JavaScript is disabled -->
|
||||||
|
<meta http-equiv="refresh" content="0; url=/" />
|
||||||
|
|
||||||
|
<!-- Canonical root reference -->
|
||||||
|
<link rel="canonical" href="/" />
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
(function redirectToRoot() {
|
||||||
|
// Configuration object with safe defaults.
|
||||||
|
var opts = {
|
||||||
|
fallbackPath: "/", // string: fallback destination if origin is unavailable
|
||||||
|
delayMs: 0, // number: delay before redirect in ms (0 = immediate)
|
||||||
|
behavior: "replace" // enum: "replace" | "assign"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determine absolute origin in all mainstream browsers.
|
||||||
|
var origin = (typeof location.origin === "string" && location.origin)
|
||||||
|
|| (location.protocol + "//" + location.host);
|
||||||
|
|
||||||
|
// Final destination: absolute root of the current site, or fallback path.
|
||||||
|
var destination = origin ? origin + "/" : opts.fallbackPath;
|
||||||
|
|
||||||
|
function go() {
|
||||||
|
if (opts.behavior === "assign") {
|
||||||
|
location.assign(destination);
|
||||||
|
} else {
|
||||||
|
location.replace(destination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute redirect, optionally after a short delay.
|
||||||
|
if (opts.delayMs > 0) {
|
||||||
|
setTimeout(go, opts.delayMs);
|
||||||
|
} else {
|
||||||
|
go();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Secondary meta-refresh for no-JS environments is already set above.
|
||||||
|
Some very old crawlers may ignore JS; the meta refresh ensures coverage.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<noscript>
|
||||||
|
<!-- Extra defense-in-depth: if JS is disabled, meta refresh (above) handles redirect. -->
|
||||||
|
<style>
|
||||||
|
html, body { height:100%; }
|
||||||
|
body { display:flex; align-items:center; justify-content:center; margin:0; font: 16px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
|
||||||
|
.msg { opacity: .75; text-align: center; }
|
||||||
|
</style>
|
||||||
|
</noscript>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="msg">Redirecting to the site root… If you are not redirected, <a href="/">click here</a>.</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user