feat(mokoonyx): tabbed profile/registration, drawer link-colour fix, retire right drawer #158
@@ -15,6 +15,18 @@
|
||||
# Changelog — MokoOnyx (VERSION: 02.28.09)
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- Tabbed user profile edit and registration screens: `com_users` fieldsets are grouped into Bootstrap tabs via the shared `layouts/mokoonyx/profile-tabset` layout (Account, Basic Settings, Passkey Login, Multi-factor Authentication, plus any plugin-contributed fieldsets such as MokoSuiteCRM / MokoSuiteCommunity)
|
||||
|
||||
### Changed
|
||||
- Drawer menu links now follow the drawer's own foreground colour (`--offcanvas-color`) instead of the header navbar link colour, so they adapt to the active theme
|
||||
|
||||
### Fixed
|
||||
- Drawer menu links were invisible (white-on-white) in the light theme; they now render correctly in both light and dark themes
|
||||
|
||||
### Removed
|
||||
- Retired the right drawer entirely: the `drawer-right` template position, its toggle button, offcanvas markup, the `drawerRightIcon` parameter, and the associated CSS / JS / RTL rules. On update, any modules still assigned to `drawer-right` are automatically moved to the `sidebar-right` position
|
||||
|
||||
## [02.28.00] --- 2026-07-13
|
||||
|
||||
## [02.27.06] --- 2026-06-29
|
||||
|
||||
+1
-24
@@ -36,7 +36,6 @@ $stickyHeader = (bool) $params->get('stickyHeader', 0);
|
||||
|
||||
// Drawer icon params (escaped)
|
||||
$params_leftIcon = htmlspecialchars($params->get('drawerLeftIcon', 'fa-solid fa-chevron-left'), ENT_QUOTES, 'UTF-8');
|
||||
$params_rightIcon = htmlspecialchars($params->get('drawerRightIcon', 'fa-solid fa-chevron-right'), ENT_QUOTES, 'UTF-8');
|
||||
|
||||
// Template/Media path
|
||||
$templatePath = 'media/templates/site/mokoonyx';
|
||||
@@ -69,7 +68,7 @@ $wa->useScript('template.js');
|
||||
$wa->useStyle('template.font.osaka');
|
||||
|
||||
// Smart Bootstrap component loading - only load what's needed
|
||||
if ($this->countModules('drawer-left', true) || $this->countModules('drawer-right', true)) {
|
||||
if ($this->countModules('drawer-left', true)) {
|
||||
// Load Bootstrap Offcanvas component for drawers
|
||||
HTMLHelper::_('bootstrap.offcanvas');
|
||||
}
|
||||
@@ -295,16 +294,6 @@ $wa->useScript('user.js'); // js/user.js
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($this->countModules('drawer-right')) : ?>
|
||||
<button class="drawer-toggle-right btn btn-outline-secondary"
|
||||
type="button"
|
||||
data-bs-toggle="offcanvas"
|
||||
data-bs-target="#drawer-right"
|
||||
aria-controls="drawer-right">
|
||||
<span class="<?php echo $params_rightIcon; ?>"></span>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($this->countModules('menu', true) || $this->countModules('search', true)) : ?>
|
||||
<div class="grid-child container-nav">
|
||||
<?php if ($this->countModules('menu', true)) : ?>
|
||||
@@ -430,18 +419,6 @@ $wa->useScript('user.js'); // js/user.js
|
||||
</aside>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($this->countModules('drawer-right', true)) : ?>
|
||||
<!-- Right Offcanvas Drawer -->
|
||||
<aside class="offcanvas offcanvas-end" tabindex="-1" id="drawer-right">
|
||||
<div class="offcanvas-header">
|
||||
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="<?php echo Text::_('JLIB_HTML_BEHAVIOR_CLOSE'); ?>"></button>
|
||||
</div>
|
||||
<div class="offcanvas-body">
|
||||
<jdoc:include type="modules" name="drawer-right" style="none" />
|
||||
</div>
|
||||
</aside>
|
||||
<?php endif; ?>
|
||||
|
||||
<jdoc:include type="modules" name="debug" style="none" />
|
||||
|
||||
</body>
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
|
||||
This file is part of a Moko Consulting project.
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
Template override of com_users profile edit: groups the form fieldsets into
|
||||
Bootstrap tabs via layouts/mokoonyx/profile-tabset. Structure otherwise mirrors
|
||||
the core template (save/cancel tasks, control fields, MFA block).
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\Layout\LayoutHelper;
|
||||
use Joomla\CMS\Router\Route;
|
||||
|
||||
/** @var Joomla\Component\Users\Site\View\Profile\HtmlView $this */
|
||||
|
||||
HTMLHelper::_('bootstrap.tooltip', '.hasTooltip');
|
||||
|
||||
// Load user_profile plugin language
|
||||
$lang = $this->getLanguage();
|
||||
$lang->load('plg_user_profile', JPATH_ADMINISTRATOR);
|
||||
|
||||
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
|
||||
$wa = $this->getDocument()->getWebAssetManager();
|
||||
$wa->useScript('keepalive')
|
||||
->useScript('form.validate');
|
||||
|
||||
// Multi-factor Authentication is rendered outside the form fieldsets by the core
|
||||
// view, so pass it to the tab-set as an extra tab (only when the UI is present).
|
||||
$extraTabs = [];
|
||||
|
||||
if ($this->mfaConfigurationUI) {
|
||||
$extraTabs[] = [
|
||||
'id' => 'mfa',
|
||||
'title' => Text::_('COM_USERS_PROFILE_MULTIFACTOR_AUTH'),
|
||||
'icon' => 'fa-solid fa-shield-halved',
|
||||
'html' => $this->mfaConfigurationUI,
|
||||
];
|
||||
}
|
||||
?>
|
||||
<div class="com-users-profile__edit profile-edit">
|
||||
<?php if ($this->params->get('show_page_heading')) : ?>
|
||||
<div class="page-header">
|
||||
<h1>
|
||||
<?php echo $this->escape($this->params->get('page_heading')); ?>
|
||||
</h1>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form id="member-profile" action="<?php echo Route::_('index.php'); ?>" method="post" class="com-users-profile__edit-form form-validate form-horizontal well" enctype="multipart/form-data">
|
||||
<?php echo LayoutHelper::render('mokoonyx.profile-tabset', [
|
||||
'form' => $this->form,
|
||||
'idBase' => 'profile',
|
||||
'extraTabs' => $extraTabs,
|
||||
]); ?>
|
||||
|
||||
<div class="com-users-profile__edit-submit control-group mt-4">
|
||||
<div class="controls">
|
||||
<button type="submit" class="btn btn-primary validate" name="task" value="profile.save">
|
||||
<span class="icon-check" aria-hidden="true"></span>
|
||||
<?php echo Text::_('JSAVE'); ?>
|
||||
</button>
|
||||
<button type="submit" class="btn btn-danger" name="task" value="profile.cancel" formnovalidate>
|
||||
<span class="icon-times" aria-hidden="true"></span>
|
||||
<?php echo Text::_('JCANCEL'); ?>
|
||||
</button>
|
||||
<input type="hidden" name="option" value="com_users">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php echo $this->form->renderControlFields(); ?>
|
||||
</form>
|
||||
</div>
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
|
||||
This file is part of a Moko Consulting project.
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
Template override of com_users registration: groups the form fieldsets into
|
||||
Bootstrap tabs via layouts/mokoonyx/profile-tabset. The captcha fieldset stays
|
||||
outside the tabs (rendered just before submit), mirroring the core template.
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\Layout\LayoutHelper;
|
||||
use Joomla\CMS\Router\Route;
|
||||
|
||||
/** @var \Joomla\Component\Users\Site\View\Registration\HtmlView $this */
|
||||
|
||||
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
|
||||
$wa = $this->getDocument()->getWebAssetManager();
|
||||
$wa->useScript('keepalive')
|
||||
->useScript('form.validate');
|
||||
?>
|
||||
<div class="com-users-registration registration">
|
||||
<?php if ($this->params->get('show_page_heading')) : ?>
|
||||
<div class="page-header">
|
||||
<h1><?php echo $this->escape($this->params->get('page_heading')); ?></h1>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form id="member-registration" action="<?php echo Route::_('index.php?task=registration.register'); ?>" method="post" class="com-users-registration__form form-validate" enctype="multipart/form-data">
|
||||
<?php // Captcha is rendered separately below, so keep it out of the tabs. ?>
|
||||
<?php echo LayoutHelper::render('mokoonyx.profile-tabset', [
|
||||
'form' => $this->form,
|
||||
'idBase' => 'registration',
|
||||
'skip' => $this->captchaEnabled ? ['captcha'] : [],
|
||||
]); ?>
|
||||
|
||||
<?php if ($this->captchaEnabled) : ?>
|
||||
<?php echo $this->form->renderFieldset('captcha'); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="com-users-registration__submit control-group mt-4">
|
||||
<div class="controls">
|
||||
<button type="submit" class="com-users-registration__register btn btn-primary validate">
|
||||
<?php echo Text::_('JREGISTER'); ?>
|
||||
</button>
|
||||
<input type="hidden" name="option" value="com_users">
|
||||
<input type="hidden" name="task" value="registration.register">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php echo $this->form->renderControlFields(); ?>
|
||||
</form>
|
||||
</div>
|
||||
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
|
||||
This file is part of a Moko Consulting project.
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Language\Text;
|
||||
|
||||
/**
|
||||
* Profile / registration tab-set layout.
|
||||
*
|
||||
* Renders a Joomla form's fieldsets as Bootstrap 5 tabs using the native
|
||||
* `uitab` HTMLHelper (which also loads the bootstrap.tab script). Any fieldset
|
||||
* contributed by a plugin — including MokoSuiteCRM / MokoSuiteCommunity user
|
||||
* plugins — automatically becomes its own tab; nothing here is hardcoded to a
|
||||
* specific field.
|
||||
*
|
||||
* Expected $displayData keys:
|
||||
* 'form' => Joomla\CMS\Form\Form The bound form to render.
|
||||
* 'idBase' => string Unique prefix for tab ids ('profile' | 'registration').
|
||||
* 'skip' => string[] Fieldset names to exclude from tabs
|
||||
* (caller renders them itself, e.g. 'captcha').
|
||||
* 'extraTabs' => array[] Extra tabs appended after the fieldset tabs.
|
||||
* Each: ['id' => string, 'title' => string,
|
||||
* 'icon' => string, 'html' => string]. Used for
|
||||
* content rendered outside the form (e.g. MFA).
|
||||
*/
|
||||
|
||||
/** @var Joomla\CMS\Form\Form $form */
|
||||
$form = $displayData['form'];
|
||||
$idBase = $displayData['idBase'] ?? 'profile';
|
||||
$skip = $displayData['skip'] ?? [];
|
||||
$extraTabs = $displayData['extraTabs'] ?? [];
|
||||
|
||||
// Collect fieldsets that actually contain fields (minus any the caller skips).
|
||||
$groups = [];
|
||||
foreach ($form->getFieldsets() as $name => $fieldset) {
|
||||
if (in_array($name, $skip, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (count($form->getFieldset($name))) {
|
||||
$groups[$name] = $fieldset;
|
||||
}
|
||||
}
|
||||
|
||||
// Per-fieldset presentation.
|
||||
// icon => Font Awesome 7 class for the tab.
|
||||
// title => OPTIONAL title override, used only when a fieldset has no label of
|
||||
// its own (e.g. a label-less "core" fieldset). May be a language key
|
||||
// or a literal string.
|
||||
//
|
||||
// A fieldset's own translated label always wins when present, so Joomla/plugin
|
||||
// wording ("Basic Settings", "Passkey Login", …) is preserved. Add entries only
|
||||
// to pin an icon or name a label-less fieldset — including MokoSuiteCRM /
|
||||
// MokoSuiteCommunity sections.
|
||||
$tabMeta = [
|
||||
'core' => ['icon' => 'fa-solid fa-user', 'title' => 'TPL_MOKOONYX_PROFILE_TAB_ACCOUNT'],
|
||||
'default' => ['icon' => 'fa-solid fa-user', 'title' => 'TPL_MOKOONYX_PROFILE_TAB_ACCOUNT'],
|
||||
'settings' => ['icon' => 'fa-solid fa-gear'],
|
||||
'params' => ['icon' => 'fa-solid fa-gear'],
|
||||
'profile' => ['icon' => 'fa-solid fa-id-card'],
|
||||
'passwordless' => ['icon' => 'fa-solid fa-fingerprint'],
|
||||
'webauthn' => ['icon' => 'fa-solid fa-fingerprint'],
|
||||
// ── MokoSuiteCRM / MokoSuiteCommunity fieldsets: add entries here ─────────
|
||||
];
|
||||
|
||||
// Resolve a fieldset to [title, icon] with graceful fallbacks.
|
||||
$resolve = static function (string $name, $fieldset) use ($tabMeta): array {
|
||||
$meta = $tabMeta[strtolower($name)] ?? [];
|
||||
$icon = $meta['icon'] ?? 'fa-solid fa-folder-open';
|
||||
|
||||
if (!empty($fieldset->label)) {
|
||||
$title = Text::_($fieldset->label);
|
||||
} elseif (!empty($meta['title'])) {
|
||||
$title = Text::_($meta['title']);
|
||||
} else {
|
||||
$title = ucwords(str_replace(['_', '-'], ' ', $name));
|
||||
}
|
||||
|
||||
return [$title, $icon];
|
||||
};
|
||||
|
||||
// Build a uniform tab list: fieldset tabs first, then any extra tabs.
|
||||
$tabs = [];
|
||||
|
||||
foreach ($groups as $name => $fieldset) {
|
||||
[$title, $icon] = $resolve($name, $fieldset);
|
||||
$tabs[] = [
|
||||
'id' => $idBase . '-' . $name,
|
||||
'title' => $title,
|
||||
'icon' => $icon,
|
||||
'html' => $form->renderFieldset($name),
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($extraTabs as $extra) {
|
||||
if (empty($extra['html'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tabs[] = [
|
||||
'id' => $idBase . '-' . ($extra['id'] ?? 'extra'),
|
||||
'title' => $extra['title'] ?? '',
|
||||
'icon' => $extra['icon'] ?? 'fa-solid fa-folder-open',
|
||||
'html' => $extra['html'],
|
||||
];
|
||||
}
|
||||
|
||||
// A single section doesn't warrant a tab strip — render it flat instead.
|
||||
if (count($tabs) <= 1) {
|
||||
foreach ($tabs as $tab) {
|
||||
echo '<fieldset>' . $tab['html'] . '</fieldset>';
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
echo HTMLHelper::_('uitab.startTabSet', $idBase, ['active' => $tabs[0]['id'], 'recall' => true]);
|
||||
|
||||
foreach ($tabs as $tab) {
|
||||
$label = '<span class="' . $tab['icon'] . ' me-2" aria-hidden="true"></span>'
|
||||
. htmlspecialchars($tab['title'], ENT_QUOTES, 'UTF-8');
|
||||
|
||||
echo HTMLHelper::_('uitab.addTab', $idBase, $tab['id'], $label);
|
||||
echo $tab['html'];
|
||||
echo HTMLHelper::_('uitab.endTab');
|
||||
}
|
||||
|
||||
echo HTMLHelper::_('uitab.endTabSet');
|
||||
+1
-25
@@ -195,11 +195,10 @@ $hasClass = '';
|
||||
if ($this->countModules('sidebar-left', true)) { $hasClass .= ' has-sidebar-left'; }
|
||||
if ($this->countModules('sidebar-right', true)) { $hasClass .= ' has-sidebar-right'; }
|
||||
if ($this->countModules('drawer-left', true)) { $hasClass .= ' has-drawer-left'; }
|
||||
if ($this->countModules('drawer-right', true)) { $hasClass .= ' has-drawer-right'; }
|
||||
|
||||
// Smart Bootstrap component loading - only load what's needed
|
||||
HTMLHelper::_('bootstrap.collapse');
|
||||
if ($this->countModules('drawer-left', true) || $this->countModules('drawer-right', true)) {
|
||||
if ($this->countModules('drawer-left', true)) {
|
||||
HTMLHelper::_('bootstrap.offcanvas');
|
||||
}
|
||||
|
||||
@@ -252,7 +251,6 @@ if ($faKitCode !== '') {
|
||||
}
|
||||
}
|
||||
$params_leftIcon = htmlspecialchars($this->params->get('drawerLeftIcon', 'fa-solid fa-chevron-left'), ENT_COMPAT, 'UTF-8');
|
||||
$params_rightIcon = htmlspecialchars($this->params->get('drawerRightIcon', 'fa-solid fa-chevron-right'), ENT_COMPAT, 'UTF-8');
|
||||
|
||||
// Load theme palette stylesheets — Joomla auto-serves .min when debug is off
|
||||
$wa->useStyle('template.light.standard');
|
||||
@@ -490,16 +488,6 @@ $wa->useScript('user.js'); // js/user.js
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($this->countModules('drawer-right')) : ?>
|
||||
<button class="drawer-toggle-right btn btn-outline-secondary"
|
||||
type="button"
|
||||
data-bs-toggle="offcanvas"
|
||||
data-bs-target="#drawer-right"
|
||||
aria-controls="drawer-right">
|
||||
<span class="<?php echo $params_rightIcon; ?>"></span>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (($this->countModules('menu', true) || $this->countModules('search', true)) && !$hideMenuHome) : ?>
|
||||
<div class="grid-child container-nav">
|
||||
<?php // Mobile: hamburger (left) + search icon (right) on one line ?>
|
||||
@@ -633,18 +621,6 @@ endif; ?>
|
||||
</aside>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($this->countModules('drawer-right', true)) : ?>
|
||||
<!-- Right Offcanvas Drawer -->
|
||||
<aside class="offcanvas offcanvas-end" tabindex="-1" id="drawer-right">
|
||||
<div class="offcanvas-header justify-content-start">
|
||||
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="<?php echo Text::_('JLIB_HTML_BEHAVIOR_CLOSE'); ?>"><span class="fa fa-close"></span></button>
|
||||
</div>
|
||||
<div class="offcanvas-body">
|
||||
<jdoc:include type="modules" name="drawer-right" style="none" />
|
||||
</div>
|
||||
</aside>
|
||||
<?php endif; ?>
|
||||
|
||||
<jdoc:include type="modules" name="debug" style="none" />
|
||||
|
||||
</body>
|
||||
|
||||
@@ -18,8 +18,6 @@ TPL_MOKOONYX_OFFLINEEMBED_DESC="In addition to the 'Offline message' defined in
|
||||
TPL_MOKOONYX_DRAWERS_FIELDSET_LABEL="Drawers"
|
||||
TPL_MOKOONYX_DRAWER_LEFT_ICON_LABEL="Drawer Left Icon CSS"
|
||||
TPL_MOKOONYX_DRAWER_LEFT_ICON_DESC="Enter the Font-Awesome class for the left drawer toggle (e.g. 'fas fa-chevron-left')."
|
||||
TPL_MOKOONYX_DRAWER_RIGHT_ICON_LABEL="Drawer Right Icon CSS"
|
||||
TPL_MOKOONYX_DRAWER_RIGHT_ICON_DESC="Enter the Font-Awesome class for the right drawer toggle (e.g. 'fas fa-chevron-right')."
|
||||
|
||||
; ===== Favicon =====
|
||||
TPL_MOKOONYX_FAVICON_FIELDSET_LABEL="Favicon"
|
||||
@@ -332,3 +330,8 @@ TPL_MOKOONYX_CSS_VARS_SOCIAL_DESC="<code>--social-icon-size</code> — Icon font
|
||||
MOD_BREADCRUMBS_HERE="YOU ARE HERE:"
|
||||
|
||||
JGLOBAL_OFFLINE="Offline"
|
||||
|
||||
; ===== User profile / registration tabs =====
|
||||
TPL_MOKOONYX_PROFILE_TAB_ACCOUNT="Account"
|
||||
TPL_MOKOONYX_PROFILE_TAB_SETTINGS="Settings"
|
||||
TPL_MOKOONYX_PROFILE_TAB_PROFILE="Profile"
|
||||
|
||||
@@ -27,5 +27,4 @@ TPL_MOKOONYX_POSITION_TOP_A="Top-a"
|
||||
TPL_MOKOONYX_POSITION_TOP_B="Top-b"
|
||||
TPL_MOKOONYX_POSITION_TOPBAR="Top Bar"
|
||||
TPL_MOKOONYX_POSITION_DRAWER_LEFT="Drawer-Left"
|
||||
TPL_MOKOONYX_POSITION_DRAWER_RIGHT="Drawer-Right"
|
||||
TPL_MOKOONYX_XML_DESCRIPTION="<p><img src=\"https://img.shields.io/gitea/v/release/MokoConsulting/MokoOnyx?gitea_url=https%3A%2F%2Fgit.mokoconsulting.tech&logo=gitea&logoColor=white&label=version\" alt=\"Version\" /> <img src=\"https://img.shields.io/badge/license-GPL--3.0--or--later-green.svg?logo=gnu&logoColor=white\" alt=\"License\" /> <img src=\"https://img.shields.io/badge/Joomla-5.x%20%7C%206.x-red.svg?logo=joomla&logoColor=white\" alt=\"Joomla\" /> <img src=\"https://img.shields.io/badge/PHP-8.1%2B-777BB4.svg?logo=php&logoColor=white\" alt=\"PHP\" /></p> <h3>MokoOnyx Template Description</h3> <p> <strong>MokoOnyx</strong> continues Joomla’s tradition of space-themed default templates— building on the legacy of <em>Solarflare</em> (Joomla 1.0), <em>Milkyway</em> (Joomla 1.5), and <em>Protostar</em> (Joomla 3.0). </p> <p> This template is a customized fork of the <strong>Cassiopeia</strong> template introduced in Joomla 4, preserving its modern, accessible, and mobile-first foundation while introducing new stylistic enhancements and structural refinements specifically tailored for use by Moko Consulting. </p> <h4>Custom Colour Themes</h4> <p> Starter palette files are included with the template. To create a custom colour scheme, copy <code>templates/mokoonyx/templates/light.custom.css</code> to <code>media/templates/site/mokoonyx/css/theme/light.custom.css</code>, or <code>templates/mokoonyx/templates/dark.custom.css</code> to <code>media/templates/site/mokoonyx/css/theme/dark.custom.css</code>. Customise the CSS variables to match your brand, then activate your palette in <em>System → Site Templates → MokoOnyx → Theme tab</em> by selecting "Custom" for the Light or Dark Mode Palette. A full variable reference is available in the <em>CSS Variables</em> tab in template options. </p> <h4>Custom CSS & JavaScript</h4> <p> For site-specific styles and scripts that should survive template updates, create the following files: </p> <ul> <li><code>media/templates/site/mokoonyx/css/user.css</code> — loaded on every page for custom CSS overrides.</li> <li><code>media/templates/site/mokoonyx/js/user.js</code> — loaded on every page for custom JavaScript.</li> </ul> <p> These files are gitignored and will not be overwritten by template updates. </p> <h4>Code Attribution</h4> <p> This template is based on the original <strong>Cassiopeia</strong> template developed by the <a href=\"https://www.joomla.org\" target=\"_blank\" rel=\"noopener\">Joomla! Project</a> and released under the GNU General Public License. </p> <p> Modifications and enhancements have been made by Moko Consulting in accordance with open-source licensing standards. </p> <p> It includes integration with <a href=\"https://afeld.github.io/bootstrap-toc/\" target=\"_blank\" rel=\"noopener\">Bootstrap TOC</a>, an open-source table of contents generator by A. Feld, licensed under the MIT License. </p> <p> All third-party libraries and assets remain the property of their respective authors and are credited within their source files where applicable. </p>"
|
||||
|
||||
@@ -18,8 +18,6 @@ TPL_MOKOONYX_OFFLINEEMBED_DESC="In addition to the 'Offline message' defined in
|
||||
TPL_MOKOONYX_DRAWERS_FIELDSET_LABEL="Drawers"
|
||||
TPL_MOKOONYX_DRAWER_LEFT_ICON_LABEL="Drawer Left Icon CSS"
|
||||
TPL_MOKOONYX_DRAWER_LEFT_ICON_DESC="Enter the Font-Awesome class for the left drawer toggle (e.g. 'fas fa-chevron-left')."
|
||||
TPL_MOKOONYX_DRAWER_RIGHT_ICON_LABEL="Drawer Right Icon CSS"
|
||||
TPL_MOKOONYX_DRAWER_RIGHT_ICON_DESC="Enter the Font-Awesome class for the right drawer toggle (e.g. 'fas fa-chevron-right')."
|
||||
|
||||
; ===== Favicon =====
|
||||
TPL_MOKOONYX_FAVICON_FIELDSET_LABEL="Favicon"
|
||||
@@ -332,3 +330,8 @@ TPL_MOKOONYX_CSS_VARS_SOCIAL_DESC="<code>--social-icon-size</code> — Icon font
|
||||
MOD_BREADCRUMBS_HERE="YOU ARE HERE:"
|
||||
|
||||
JGLOBAL_OFFLINE="Offline"
|
||||
|
||||
; ===== User profile / registration tabs =====
|
||||
TPL_MOKOONYX_PROFILE_TAB_ACCOUNT="Account"
|
||||
TPL_MOKOONYX_PROFILE_TAB_SETTINGS="Settings"
|
||||
TPL_MOKOONYX_PROFILE_TAB_PROFILE="Profile"
|
||||
|
||||
@@ -27,5 +27,4 @@ TPL_MOKOONYX_POSITION_TOP_A="Top-a"
|
||||
TPL_MOKOONYX_POSITION_TOP_B="Top-b"
|
||||
TPL_MOKOONYX_POSITION_TOPBAR="Top Bar"
|
||||
TPL_MOKOONYX_POSITION_DRAWER_LEFT="Drawer-Left"
|
||||
TPL_MOKOONYX_POSITION_DRAWER_RIGHT="Drawer-Right"
|
||||
TPL_MOKOONYX_XML_DESCRIPTION="<h3>MokoOnyx Template Description</h3> <p> <strong>MokoOnyx</strong> continues Joomla’s tradition of space-themed default templates— building on the legacy of <em>Solarflare</em> (Joomla 1.0), <em>Milkyway</em> (Joomla 1.5), and <em>Protostar</em> (Joomla 3.0). </p> <p> This template is a customized fork of the <strong>Cassiopeia</strong> template introduced in Joomla 4, preserving its modern, accessible, and mobile-first foundation while introducing new stylistic enhancements and structural refinements specifically tailored for use by Moko Consulting. </p> <h4>Custom Color Themes</h4> <p> Starter palette files are included with the template. To create a custom color scheme, copy <code>templates/mokoonyx/templates/light.custom.css</code> to <code>media/templates/site/mokoonyx/css/theme/light.custom.css</code>, or <code>templates/mokoonyx/templates/dark.custom.css</code> to <code>media/templates/site/mokoonyx/css/theme/dark.custom.css</code>. Customize the CSS variables to match your brand, then activate your palette in <em>System → Site Templates → MokoOnyx → Theme tab</em> by selecting "Custom" for the Light or Dark Mode Palette. A full variable reference is available in the <em>CSS Variables</em> tab in template options. </p> <h4>Custom CSS & JavaScript</h4> <p> For site-specific styles and scripts that should survive template updates, create the following files: </p> <ul> <li><code>media/templates/site/mokoonyx/css/user.css</code> — loaded on every page for custom CSS overrides.</li> <li><code>media/templates/site/mokoonyx/js/user.js</code> — loaded on every page for custom JavaScript.</li> </ul> <p> These files are gitignored and will not be overwritten by template updates. </p> <h4>Code Attribution</h4> <p> This template is based on the original <strong>Cassiopeia</strong> template developed by the <a href=\"https://www.joomla.org\" target=\"_blank\" rel=\"noopener\">Joomla! Project</a> and released under the GNU General Public License. </p> <p> Modifications and enhancements have been made by Moko Consulting in accordance with open-source licensing standards. </p> <p> It includes integration with <a href=\"https://afeld.github.io/bootstrap-toc/\" target=\"_blank\" rel=\"noopener\">Bootstrap TOC</a>, an open-source table of contents generator by A. Feld, licensed under the MIT License. </p> <p> All third-party libraries and assets remain the property of their respective authors and are credited within their source files where applicable. </p>"
|
||||
|
||||
@@ -33,18 +33,6 @@
|
||||
padding-right: calc(var(--btn-padding-x)*0.5) !important;
|
||||
}
|
||||
|
||||
[dir="rtl"] .drawer-toggle-right {
|
||||
|
||||
position: fixed;
|
||||
top: 250px;
|
||||
left: 0px;
|
||||
z-index: 1050;
|
||||
background-color: var(--nav-bg-color) !important;
|
||||
color: var(--nav-text-color) !important;
|
||||
padding-left: calc(var(--btn-padding-x)*0.5) !important;
|
||||
padding-right: calc(var(--btn-padding-x)*0.5) !important;
|
||||
}
|
||||
|
||||
[dir="rtl"] ol,
|
||||
[dir="rtl"] ul {
|
||||
|
||||
|
||||
@@ -96,8 +96,7 @@ form {
|
||||
color: var(--color-link, #224FAA);
|
||||
}
|
||||
|
||||
.offcanvas-start,
|
||||
.offcanvas-end {
|
||||
.offcanvas-start {
|
||||
width: 280px;
|
||||
}
|
||||
|
||||
@@ -116,19 +115,23 @@ button.drawer-toggle-left,
|
||||
border-bottom-left-radius: 0 !important;
|
||||
}
|
||||
|
||||
button.drawer-toggle-right,
|
||||
.drawer-toggle-right.btn {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
right: 0px;
|
||||
z-index: 1050;
|
||||
background-color: var(--accent-color, var(--color-primary, #2a69b8)) !important;
|
||||
color: var(--accent-color-text, #fff) !important;
|
||||
border-color: var(--accent-color, var(--color-primary, #2a69b8)) !important;
|
||||
padding-left: .5rem;
|
||||
padding-right: .5rem;
|
||||
border-top-right-radius: 0 !important;
|
||||
border-bottom-right-radius: 0 !important;
|
||||
/* Drawer menu links must follow the drawer's own foreground colour
|
||||
(--offcanvas-color), not the header navbar's link colour, which is white to
|
||||
sit on the coloured navbar. Without this, menu links render white-on-white in
|
||||
the light theme (invisible) and only look right in dark by coincidence. */
|
||||
.offcanvas .mod-menu a,
|
||||
.offcanvas .mod-menu span,
|
||||
.offcanvas .mod-menu button,
|
||||
.offcanvas .mod-menu .nav-link,
|
||||
.offcanvas .mod-menu .metismenu-item > a,
|
||||
.offcanvas .mod-menu .metismenu-item > span {
|
||||
color: var(--offcanvas-color, var(--body-color, #22262a)) !important;
|
||||
}
|
||||
|
||||
.offcanvas .mod-menu a:hover,
|
||||
.offcanvas .mod-menu a:focus,
|
||||
.offcanvas .mod-menu .metismenu-item > a:hover {
|
||||
color: var(--color-hover, var(--accent-color-primary, #3f8ff0)) !important;
|
||||
}
|
||||
|
||||
hr {
|
||||
|
||||
@@ -506,7 +506,7 @@
|
||||
*/
|
||||
function initDrawers() {
|
||||
// Check if any drawer buttons exist before initializing
|
||||
var hasDrawers = doc.querySelector(".drawer-toggle-left") || doc.querySelector(".drawer-toggle-right");
|
||||
var hasDrawers = doc.querySelector(".drawer-toggle-left");
|
||||
if (!hasDrawers) {
|
||||
return; // No drawers, skip initialization
|
||||
}
|
||||
|
||||
@@ -105,9 +105,56 @@ class Tpl_MokoonyxInstallerScript implements InstallerScriptInterface
|
||||
$this->lockExtension();
|
||||
}
|
||||
|
||||
if ($type === 'update') {
|
||||
$this->migrateRightDrawerModules();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate modules from the retired drawer-right position to sidebar-right.
|
||||
*
|
||||
* The drawer-right template position and its markup were removed. Rather
|
||||
* than stranding modules still assigned to it (they would silently stop
|
||||
* displaying), move them to the sidebar-right position so they remain
|
||||
* visible. Published and unpublished site modules are both moved so nothing
|
||||
* is lost. Runs on update and is naturally idempotent — once migrated, no
|
||||
* rows match drawer-right on subsequent updates.
|
||||
*/
|
||||
private function migrateRightDrawerModules(): void
|
||||
{
|
||||
try {
|
||||
$db = Factory::getDbo();
|
||||
|
||||
$query = $db->getQuery(true)
|
||||
->update($db->quoteName('#__modules'))
|
||||
->set($db->quoteName('position') . ' = ' . $db->quote('sidebar-right'))
|
||||
->where($db->quoteName('position') . ' = ' . $db->quote('drawer-right'))
|
||||
->where($db->quoteName('client_id') . ' = 0');
|
||||
$db->setQuery($query)->execute();
|
||||
|
||||
$moved = $db->getAffectedRows();
|
||||
|
||||
if ($moved > 0) {
|
||||
Factory::getApplication()->enqueueMessage(
|
||||
sprintf(
|
||||
'<strong>MokoOnyx: The right drawer has been retired.</strong> '
|
||||
. '%d module(s) were moved from the removed <code>drawer-right</code> position to '
|
||||
. '<code>sidebar-right</code>. Review their placement and adjust if needed.',
|
||||
$moved
|
||||
),
|
||||
'notice'
|
||||
);
|
||||
$this->logMessage("Right drawer retired — moved {$moved} module(s) from drawer-right to sidebar-right.");
|
||||
} else {
|
||||
$this->logMessage('Right drawer retired — no modules were assigned to drawer-right.');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$this->logMessage('Right drawer module migration failed: ' . $e->getMessage(), 'warning');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace MokoCassiopeia references in article content and module content.
|
||||
*/
|
||||
|
||||
@@ -92,7 +92,6 @@
|
||||
<position>offline</position>
|
||||
<position>offline-footer</position>
|
||||
<position>drawer-left</position>
|
||||
<position>drawer-right</position>
|
||||
</positions>
|
||||
<languages>
|
||||
<language tag="en-GB">language/en-GB/tpl_mokoonyx.ini</language>
|
||||
@@ -144,7 +143,6 @@
|
||||
<!-- Drawers tab -->
|
||||
<fieldset name="drawers" label="TPL_MOKOONYX_DRAWERS_FIELDSET_LABEL">
|
||||
<field name="drawerLeftIcon" type="text" default="fa-solid fa-chevron-right" label="TPL_MOKOONYX_DRAWER_LEFT_ICON_LABEL" description="TPL_MOKOONYX_DRAWER_LEFT_ICON_DESC" filter="string" />
|
||||
<field name="drawerRightIcon" type="text" default="fa-solid fa-chevron-left" label="TPL_MOKOONYX_DRAWER_RIGHT_ICON_LABEL" description="TPL_MOKOONYX_DRAWER_RIGHT_ICON_DESC" filter="string" />
|
||||
</fieldset>
|
||||
|
||||
<!-- THEME TAB (all style/theme settings grouped) -->
|
||||
|
||||
Reference in New Issue
Block a user