Add mobile-responsive overrides for 20 modules and 7 component views #81

Merged
Copilot merged 14 commits from copilot/make-mod-search-mobile-responsive into main 2026-02-26 15:49:03 +00:00
16 changed files with 1292 additions and 0 deletions
Showing only changes of commit fb4bdbeace - Show all commits

View File

@@ -20423,3 +20423,561 @@ nav[data-toggle=toc] .nav-link.active+ul{
text-align: left;
}
}
/* ===== ADDITIONAL KUNENA & MEMBERSHIP PRO MODULE STYLES ===== */
/* === mod_kunenalogin (Kunena Login) === */
.mod-kunena-login-responsive {
width: 100%;
}
.mod-kunena-login__profile {
display: flex;
align-items: center;
gap: 1rem;
padding: 1rem;
background: var(--secondary-bg);
border-radius: var(--border-radius);
margin-bottom: 1rem;
}
.mod-kunena-login__avatar {
flex-shrink: 0;
width: 60px;
height: 60px;
border-radius: 50%;
overflow: hidden;
}
.mod-kunena-login__avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.mod-kunena-login__user-info {
flex: 1;
}
.mod-kunena-login__username {
font-weight: 600;
font-size: 1.125rem;
margin-bottom: 0.25rem;
}
.mod-kunena-login__username a {
color: var(--body-color);
text-decoration: none;
}
.mod-kunena-login__username a:hover {
color: var(--color-primary);
}
.mod-kunena-login__rank {
font-size: 0.875rem;
color: var(--gray-600);
}
.mod-kunena-login__stats {
display: flex;
gap: 1.5rem;
padding: 1rem;
background: var(--body-bg);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
margin-bottom: 1rem;
}
.mod-kunena-login__stat {
display: flex;
gap: 0.5rem;
align-items: center;
}
.mod-kunena-login__stat-label {
color: var(--gray-600);
font-size: 0.875rem;
}
.mod-kunena-login__stat-value {
font-weight: 600;
color: var(--body-color);
}
.mod-kunena-login__form {
display: flex;
flex-direction: column;
gap: 1rem;
}
.mod-kunena-login__pretext,
.mod-kunena-login__posttext {
font-size: 0.875rem;
color: var(--gray-600);
line-height: 1.6;
}
.mod-kunena-login__fields {
display: flex;
flex-direction: column;
gap: 1rem;
}
.mod-kunena-login__field {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.mod-kunena-login__label {
font-weight: 600;
font-size: 0.875rem;
color: var(--body-color);
}
.mod-kunena-login__input {
padding: 0.5rem 0.75rem;
font-size: 1rem;
line-height: 1.5;
border: 1px solid var(--input-border-color, #dee2e6);
border-radius: var(--border-radius);
background: var(--input-bg, #fff);
color: var(--input-color, #212529);
min-height: 44px;
}
.mod-kunena-login__input:focus {
border-color: var(--color-primary);
outline: 0;
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
}
.mod-kunena-login__remember {
display: flex;
align-items: center;
gap: 0.5rem;
}
.mod-kunena-login__checkbox {
width: 20px;
height: 20px;
cursor: pointer;
}
.mod-kunena-login__remember-label {
font-size: 0.875rem;
cursor: pointer;
margin: 0;
}
.mod-kunena-login__actions {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.mod-kunena-login__btn {
padding: 0.625rem 1rem;
font-size: 1rem;
font-weight: 600;
border-radius: var(--border-radius);
min-height: 44px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
transition: all 0.2s;
border: none;
cursor: pointer;
text-decoration: none;
position: relative;
}
.mod-kunena-login__badge {
position: absolute;
top: -8px;
right: -8px;
background: var(--danger);
color: white;
border-radius: 50%;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.75rem;
font-weight: 700;
}
.mod-kunena-login__logout-form {
width: 100%;
}
.mod-kunena-login__links {
display: flex;
flex-direction: column;
gap: 0.5rem;
margin-top: 0.5rem;
}
.mod-kunena-login__link {
color: var(--link-color);
text-decoration: none;
font-size: 0.875rem;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.5rem;
border-radius: var(--border-radius);
transition: background 0.2s;
}
.mod-kunena-login__link:hover {
background: var(--secondary-bg);
color: var(--link-hover-color);
}
/* === mod_kunenasearch (Kunena Search) === */
.mod-kunena-search-responsive {
width: 100%;
}
.mod-kunena-search__form {
display: flex;
gap: 0.5rem;
width: 100%;
}
.mod-kunena-search__form--button-top,
.mod-kunena-search__form--button-bottom {
flex-direction: column;
}
.mod-kunena-search__form--button-left {
flex-direction: row-reverse;
}
.mod-kunena-search__form--button-right {
flex-direction: row;
}
.mod-kunena-search__input-wrapper {
flex: 1;
}
.mod-kunena-search__input {
width: 100%;
padding: 0.5rem 0.75rem;
font-size: 1rem;
line-height: 1.5;
border: 1px solid var(--input-border-color, #dee2e6);
border-radius: var(--border-radius);
background: var(--input-bg, #fff);
color: var(--input-color, #212529);
min-height: 44px;
}
.mod-kunena-search__input:focus {
border-color: var(--color-primary);
outline: 0;
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
}
.mod-kunena-search__button {
padding: 0.5rem 1rem;
min-height: 44px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
.mod-kunena-search__form--button-top .mod-kunena-search__button,
.mod-kunena-search__form--button-bottom .mod-kunena-search__button {
width: 100%;
}
/* === mod_kunenastats (Kunena Statistics) === */
.mod-kunena-stats-responsive {
width: 100%;
}
.mod-kunena-stats__container {
display: grid;
gap: 1rem;
grid-template-columns: 1fr;
}
.mod-kunena-stats__stat {
display: flex;
align-items: center;
gap: 1rem;
padding: 1rem;
background: var(--body-bg);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
transition: all 0.2s;
}
.mod-kunena-stats__stat:hover {
background: var(--secondary-bg);
border-color: var(--color-primary);
}
.mod-kunena-stats__icon {
font-size: 2rem;
color: var(--color-primary);
flex-shrink: 0;
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
background: var(--secondary-bg);
border-radius: 50%;
}
.mod-kunena-stats__content {
flex: 1;
}
.mod-kunena-stats__value {
font-size: 1.75rem;
font-weight: 700;
color: var(--body-color);
line-height: 1;
margin-bottom: 0.25rem;
}
.mod-kunena-stats__value--link a {
color: var(--color-primary);
text-decoration: none;
font-size: 1.125rem;
}
.mod-kunena-stats__value--link a:hover {
text-decoration: underline;
}
.mod-kunena-stats__label {
font-size: 0.875rem;
color: var(--gray-600);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.mod-kunena-stats__stat--latest-member .mod-kunena-stats__label {
margin-bottom: 0.25rem;
}
/* === mod_osmembership (OS Membership Pro) === */
.mod-osmembership-responsive {
width: 100%;
}
.mod-osmembership__plans {
display: grid;
gap: 2rem;
grid-template-columns: 1fr;
}
.mod-osmembership__plan {
background: var(--body-bg);
border: 2px solid var(--border-color);
border-radius: var(--border-radius);
overflow: hidden;
transition: all 0.3s;
display: flex;
flex-direction: column;
}
.mod-osmembership__plan:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
border-color: var(--color-primary);
}
.mod-osmembership__plan-image {
width: 100%;
overflow: hidden;
}
.mod-osmembership__plan-image img {
width: 100%;
height: auto;
display: block;
}
.mod-osmembership__plan-content {
padding: 2rem;
flex: 1;
display: flex;
flex-direction: column;
}
.mod-osmembership__plan-title {
margin: 0 0 1rem 0;
font-weight: 700;
font-size: 1.5rem;
line-height: 1.3;
}
.mod-osmembership__plan-description {
color: var(--gray-600);
line-height: 1.6;
margin-bottom: 1.5rem;
}
.mod-osmembership__plan-pricing {
margin-bottom: 1.5rem;
}
.mod-osmembership__price {
display: flex;
align-items: baseline;
gap: 0.5rem;
flex-wrap: wrap;
}
.mod-osmembership__currency {
font-size: 1.5rem;
color: var(--color-primary);
font-weight: 600;
}
.mod-osmembership__amount {
font-size: 2.5rem;
font-weight: 700;
color: var(--color-primary);
line-height: 1;
}
.mod-osmembership__period {
color: var(--gray-600);
font-size: 1rem;
}
.mod-osmembership__price--free {
font-size: 2rem;
font-weight: 700;
color: var(--success);
}
.mod-osmembership__features {
flex: 1;
margin-bottom: 1.5rem;
}
.mod-osmembership__features-list {
list-style: none;
padding: 0;
margin: 0;
}
.mod-osmembership__feature {
padding: 0.5rem 0;
display: flex;
align-items: flex-start;
gap: 0.5rem;
}
.mod-osmembership__feature .icon-check {
color: var(--success);
flex-shrink: 0;
margin-top: 0.25rem;
}
.mod-osmembership__actions {
margin-top: auto;
}
.mod-osmembership__btn {
width: 100%;
min-height: 48px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
text-decoration: none;
}
.mod-osmembership__all-plans {
margin-top: 2rem;
text-align: center;
}
.mod-osmembership__all-plans-link {
min-height: 44px;
display: inline-flex;
align-items: center;
justify-content: center;
}
.mod-osmembership__empty {
text-align: center;
padding: 2rem 1rem;
background: var(--secondary-bg);
border-radius: var(--border-radius);
color: var(--gray-600);
}
/* === Mobile Responsive Adjustments === */
@media (max-width: 575.98px) {
.mod-kunena-login__input {
font-size: 16px;
min-height: 48px;
padding: 0.75rem 1rem;
}
.mod-kunena-login__btn {
min-height: 48px;
}
.mod-kunena-login__stats {
flex-direction: column;
gap: 0.75rem;
}
.mod-kunena-search__input {
font-size: 16px;
min-height: 48px;
padding: 0.75rem 1rem;
}
.mod-kunena-search__button {
min-height: 48px;
}
.mod-kunenastats__container {
grid-template-columns: 1fr;
}
}
/* Tablet adjustments */
@media (min-width: 576px) {
.mod-kunena-stats__container {
grid-template-columns: repeat(2, 1fr);
}
.mod-osmembership__plans {
grid-template-columns: repeat(2, 1fr);
}
}
/* Desktop enhancements */
@media (min-width: 768px) {
.mod-kunena-login__actions {
flex-direction: row;
}
.mod-kunenastats__container {
grid-template-columns: repeat(3, 1fr);
}
}
@media (min-width: 992px) {
.mod-osmembership__plans {
grid-template-columns: repeat(3, 1fr);
}
}

View File

@@ -42,6 +42,55 @@ Kunena latest posts module with:
- Responsive card layouts
- Touch-friendly post links
### 5. mod_kunenalogin (Kunena Forum)
Kunena login module featuring:
- Login and logout states
- User profile with avatar
- Post count and karma display
- Profile and private messages links
- Touch-friendly form controls
- Password recovery links
### 6. mod_kunenasearch (Kunena Forum)
Kunena forum search module with:
- Configurable button positions (top/right/bottom/left)
- Touch-friendly search input (48px on mobile)
- 16px input font (prevents iOS zoom)
- Icon-only or text button options
- Accessible search form
### 7. mod_kunenastats (Kunena Forum)
Kunena statistics module offering:
- Member count with icon
- Latest member display
- Message and topic counts
- Today/yesterday topic counts
- Responsive grid layout (1-3 columns)
- Visual stat cards with icons
### 8. mod_osmembership (OS Membership Pro)
Membership Pro plans module with:
- Responsive pricing cards
- Plan images and descriptions
- Feature lists with checkmarks
- Pricing display with periods
- Free plan highlighting
- Touch-friendly subscribe buttons
- Grid layout (1-3 columns)
## Component Overrides
### com_kunena (Kunena Forum)
- Category list view with responsive cards
- Mobile-optimized forum navigation
- Touch-friendly category links
### com_osmembership (OS Membership Pro)
- Plans list with pricing table layout
- Featured plan highlighting
- Responsive grid (1-4 columns)
- Touch-friendly subscription buttons
## Mobile Responsive Features
### Touch Target Sizes (WCAG 2.1 Compliant)

View File

@@ -0,0 +1,70 @@
<?php
/**
* @package Kunena
* @subpackage com_kunena
*
* @copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*
* Mobile responsive override for Kunena category list
*/
defined('_JEXEC') or die;
use Joomla\CMS\Language\Text;
$this->document->addStyleDeclaration('
.kunena-category-list-responsive {
width: 100%;
}
.kunena-category-responsive {
background: var(--body-bg);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
padding: 1rem;
margin-bottom: 1rem;
transition: all 0.2s;
}
.kunena-category-responsive:hover {
background: var(--secondary-bg);
border-color: var(--color-primary);
}
@media (max-width: 575.98px) {
.kunena-category-responsive {
padding: 0.75rem;
}
}
');
copilot-pull-request-reviewer[bot] commented 2026-02-26 15:58:15 +00:00 (Migrated from github.com)
Review

This file uses addStyleDeclaration() for CSS injection. According to the PR description, CSS should be in template.css sections with BEM naming. This approach:

  1. Makes styles harder to override and maintain
  2. Violates the stated architectural pattern of using external CSS with BEM naming
  3. Is inconsistent with module overrides which use external CSS

Consider moving all styles to the template.css file as done with module overrides in this PR, or document why this component requires a different approach.


This file uses `addStyleDeclaration()` for CSS injection. According to the PR description, CSS should be in template.css sections with BEM naming. This approach: 1. Makes styles harder to override and maintain 2. Violates the stated architectural pattern of using external CSS with BEM naming 3. Is inconsistent with module overrides which use external CSS Consider moving all styles to the template.css file as done with module overrides in this PR, or document why this component requires a different approach. ```suggestion ```
?>
<div class="kunena-category-list-responsive">
<?php if (!empty($this->categories)) : ?>
<?php foreach ($this->categories as $category) : ?>
<div class="kunena-category-responsive">
<h3 class="kunena-category__title">
<a href="<?php echo $category->getUrl(); ?>">
<?php echo $this->escape($category->name); ?>
</a>
</h3>
<?php if ($category->description) : ?>
<div class="kunena-category__description">
<?php echo $category->displayField('description'); ?>
</div>
<?php endif; ?>
<div class="kunena-category__meta">
<span><?php echo Text::_('COM_KUNENA_TOPICS'); ?>: <?php echo $category->numTopics; ?></span>
<span><?php echo Text::_('COM_KUNENA_POSTS'); ?>: <?php echo $category->numPosts; ?></span>
</div>
</div>
<?php endforeach; ?>
<?php else : ?>
<div class="alert alert-info">
<?php echo Text::_('COM_KUNENA_NO_CATEGORIES'); ?>
</div>
<?php endif; ?>
</div>

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,141 @@
<?php
/**
* @package OS Membership Pro
* @subpackage com_osmembership
*
* @copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*
* Mobile responsive override for OS Membership plans list
*/
defined('_JEXEC') or die;
use Joomla\CMS\Language\Text;
$this->document->addStyleDeclaration('
.osmembership-plans-responsive {
display: grid;
gap: 2rem;
grid-template-columns: 1fr;
}
.osmembership-plan-card {
background: var(--body-bg);
border: 2px solid var(--border-color);
border-radius: var(--border-radius);
padding: 2rem;
transition: all 0.3s;
display: flex;
flex-direction: column;
}
.osmembership-plan-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
border-color: var(--color-primary);
}
.osmembership-plan-card--featured {
border-color: var(--color-primary);
position: relative;
}
.osmembership-plan-card--featured::before {
content: "' . Text::_('OSM_POPULAR') . '";
position: absolute;
top: -12px;
right: 20px;
background: var(--color-primary);
color: white;
padding: 0.25rem 1rem;
border-radius: 1rem;
font-size: 0.875rem;
font-weight: 600;
}
@media (min-width: 768px) {
.osmembership-plans-responsive {
grid-template-columns: repeat(2, 1fr);
}
}
@media (min-width: 992px) {
.osmembership-plans-responsive {
grid-template-columns: repeat(3, 1fr);
}
}
@media (min-width: 1200px) {
.osmembership-plans-responsive.osmembership-plans--many {
grid-template-columns: repeat(4, 1fr);
}
}
');
?>
<div class="osmembership-plans-responsive <?php echo count($this->items) > 3 ? 'osmembership-plans--many' : ''; ?>">
<?php foreach ($this->items as $item) : ?>
<div class="osmembership-plan-card <?php echo $item->featured ? 'osmembership-plan-card--featured' : ''; ?>">
<?php if (!empty($item->image)) : ?>
<div class="osmembership-plan__image" style="margin-bottom: 1.5rem;">
<img src="<?php echo $item->image; ?>"
alt="<?php echo htmlspecialchars($item->title, ENT_COMPAT, 'UTF-8'); ?>"
style="width: 100%; height: auto; border-radius: var(--border-radius);" />
</div>
<?php endif; ?>
<h2 class="osmembership-plan__title" style="margin: 0 0 1rem 0; font-size: 1.75rem; font-weight: 700;">
<?php echo htmlspecialchars($item->title, ENT_COMPAT, 'UTF-8'); ?>
</h2>
<div class="osmembership-plan__pricing" style="margin-bottom: 1.5rem;">
<?php if ($item->price > 0) : ?>
<div style="font-size: 2.5rem; font-weight: 700; color: var(--color-primary); line-height: 1;">
<span style="font-size: 1.5rem; vertical-align: super;"><?php echo $this->config->currency_symbol; ?></span>
<?php echo number_format($item->price, 0); ?>
</div>
<?php if ($item->subscription_length > 0) : ?>
<div style="color: var(--gray-600); margin-top: 0.5rem;">
<?php echo Text::_('OSM_PER') . ' ' . $item->subscription_length . ' ' . Text::_('OSM_' . strtoupper($item->subscription_length_unit)); ?>
</div>
<?php endif; ?>
<?php else : ?>
<div style="font-size: 2.5rem; font-weight: 700; color: var(--success);">
<?php echo Text::_('OSM_FREE'); ?>
</div>
<?php endif; ?>
</div>
<?php if (!empty($item->short_description)) : ?>
<div class="osmembership-plan__description" style="margin-bottom: 1.5rem; color: var(--gray-600); line-height: 1.6;">
<?php echo $item->short_description; ?>
</div>
<?php endif; ?>
<?php if (!empty($item->features)) : ?>
<div class="osmembership-plan__features" style="flex: 1; margin-bottom: 1.5rem;">
<ul style="list-style: none; padding: 0; margin: 0;">
<?php foreach (explode("\n", $item->features) as $feature) : ?>
<?php if (trim($feature)) : ?>
<li style="padding: 0.5rem 0; display: flex; align-items: flex-start; gap: 0.5rem;">
<span class="icon-check" style="color: var(--success); flex-shrink: 0; margin-top: 0.25rem;"></span>
<span><?php echo htmlspecialchars(trim($feature), ENT_COMPAT, 'UTF-8'); ?></span>
</li>
<?php endif; ?>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<div class="osmembership-plan__actions">
<a href="<?php echo OSMembershipHelperRoute::getRegistrationRoute($item->id); ?>"
class="btn btn-primary"
style="width: 100%; min-height: 48px; display: inline-flex; align-items: center; justify-content: center; text-decoration: none;">
<?php echo Text::_('OSM_SUBSCRIBE_NOW'); ?>
<span class="icon-chevron-right" style="margin-left: 0.5rem;"></span>
</a>
</div>
</div>
<?php endforeach; ?>
</div>
copilot-pull-request-reviewer[bot] commented 2026-02-26 15:58:14 +00:00 (Migrated from github.com)
Review

This file uses both addStyleDeclaration() for CSS injection and inline styles on HTML elements. According to the PR description, CSS should be in template.css sections with BEM naming. This approach:

  1. Mixes concerns - styles are split between the component and inline
  2. Makes styles harder to override and maintain
  3. Violates the stated architectural pattern of using external CSS with BEM naming

Consider moving all styles to the template.css file as done with other components in this PR, or document why this component requires a different approach.

This file uses both `addStyleDeclaration()` for CSS injection and inline styles on HTML elements. According to the PR description, CSS should be in template.css sections with BEM naming. This approach: 1. Mixes concerns - styles are split between the component and inline 2. Makes styles harder to override and maintain 3. Violates the stated architectural pattern of using external CSS with BEM naming Consider moving all styles to the template.css file as done with other components in this PR, or document why this component requires a different approach.

View File

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

View File

@@ -0,0 +1,187 @@
<?php
/**
* @package Kunena
* @subpackage mod_kunenalogin
*
* @copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*
* Mobile responsive override for mod_kunenalogin module
*/
defined('_JEXEC') or die;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;
$moduleclass_sfx = htmlspecialchars($params->get('moduleclass_sfx', ''), ENT_COMPAT, 'UTF-8');
// Add responsive wrapper class
$wrapperClass = 'mod-kunena-login mod-kunena-login-responsive ' . $moduleclass_sfx;
?>
<div class="<?php echo $wrapperClass; ?>">
<?php if ($kunena_my->exists()) : ?>
<!-- Logged in state -->
<div class="mod-kunena-login__profile">
<?php if ($params->get('showAvatar', 1)) : ?>
<div class="mod-kunena-login__avatar">
<?php echo $kunena_my->getAvatarImage('', 60, 60); ?>
</div>
<?php endif; ?>
<div class="mod-kunena-login__user-info">
<div class="mod-kunena-login__username">
<a href="<?php echo $kunena_my->getURL(); ?>">
<?php echo $kunena_my->getName(); ?>
</a>
</div>
<?php if ($params->get('showRank', 1) && !empty($kunena_my->getRank())) : ?>
<div class="mod-kunena-login__rank">
<?php echo $kunena_my->getRank(); ?>
</div>
<?php endif; ?>
</div>
</div>
<?php if ($params->get('showStats', 1)) : ?>
<div class="mod-kunena-login__stats">
<div class="mod-kunena-login__stat">
<span class="mod-kunena-login__stat-label"><?php echo Text::_('MOD_KUNENALOGIN_POSTS'); ?>:</span>
<span class="mod-kunena-login__stat-value"><?php echo $kunena_my->posts; ?></span>
</div>
<?php if ($params->get('showKarma', 0) && isset($kunena_my->karma)) : ?>
<div class="mod-kunena-login__stat">
<span class="mod-kunena-login__stat-label"><?php echo Text::_('MOD_KUNENALOGIN_KARMA'); ?>:</span>
<span class="mod-kunena-login__stat-value"><?php echo $kunena_my->karma; ?></span>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
<div class="mod-kunena-login__actions">
<?php if ($params->get('showProfile', 1)) : ?>
<a href="<?php echo $kunena_my->getURL(); ?>" class="mod-kunena-login__btn btn btn-secondary">
<span class="icon-user" aria-hidden="true"></span>
<?php echo Text::_('MOD_KUNENALOGIN_PROFILE'); ?>
</a>
<?php endif; ?>
<?php if ($params->get('showMessages', 1)) : ?>
<a href="<?php echo KunenaRoute::_('index.php?option=com_kunena&view=user&layout=messages'); ?>"
class="mod-kunena-login__btn btn btn-secondary">
<span class="icon-envelope" aria-hidden="true"></span>
<?php echo Text::_('MOD_KUNENALOGIN_PRIVATE_MESSAGES'); ?>
<?php if (!empty($private_messages)) : ?>
<span class="mod-kunena-login__badge"><?php echo $private_messages; ?></span>
<?php endif; ?>
</a>
<?php endif; ?>
<form action="<?php echo Route::_('index.php', true); ?>" method="post" class="mod-kunena-login__logout-form">
<button type="submit" class="mod-kunena-login__btn mod-kunena-login__btn--logout btn btn-primary">
<span class="icon-sign-out" aria-hidden="true"></span>
<?php echo Text::_('MOD_KUNENALOGIN_LOGOUT'); ?>
</button>
<input type="hidden" name="option" value="com_users" />
<input type="hidden" name="task" value="user.logout" />
<input type="hidden" name="return" value="<?php echo $return; ?>" />
<?php echo JHtml::_('form.token'); ?>
</form>
</div>
<?php else : ?>
<!-- Login form -->
<form action="<?php echo Route::_('index.php', true); ?>" method="post" class="mod-kunena-login__form">
<?php if ($params->get('pretext')) : ?>
<div class="mod-kunena-login__pretext">
<?php echo $params->get('pretext'); ?>
</div>
<?php endif; ?>
<div class="mod-kunena-login__fields">
<div class="mod-kunena-login__field">
<label for="kunena-login-username-<?php echo $module->id; ?>" class="mod-kunena-login__label">
<?php echo Text::_('MOD_KUNENALOGIN_USERNAME'); ?>
</label>
<input
id="kunena-login-username-<?php echo $module->id; ?>"
type="text"
name="username"
class="mod-kunena-login__input form-control"
placeholder="<?php echo Text::_('MOD_KUNENALOGIN_USERNAME'); ?>"
autocomplete="username"
required
/>
</div>
<div class="mod-kunena-login__field">
<label for="kunena-login-password-<?php echo $module->id; ?>" class="mod-kunena-login__label">
<?php echo Text::_('MOD_KUNENALOGIN_PASSWORD'); ?>
</label>
<input
id="kunena-login-password-<?php echo $module->id; ?>"
type="password"
name="password"
class="mod-kunena-login__input form-control"
placeholder="<?php echo Text::_('MOD_KUNENALOGIN_PASSWORD'); ?>"
autocomplete="current-password"
required
/>
</div>
<?php if ($params->get('showRememberMe', 1)) : ?>
<div class="mod-kunena-login__remember">
<input
id="kunena-login-remember-<?php echo $module->id; ?>"
type="checkbox"
name="remember"
class="mod-kunena-login__checkbox"
value="yes"
/>
<label for="kunena-login-remember-<?php echo $module->id; ?>" class="mod-kunena-login__remember-label">
<?php echo Text::_('MOD_KUNENALOGIN_REMEMBER_ME'); ?>
</label>
</div>
<?php endif; ?>
</div>
<div class="mod-kunena-login__actions">
<button type="submit" class="mod-kunena-login__btn mod-kunena-login__btn--submit btn btn-primary">
<span class="icon-sign-in" aria-hidden="true"></span>
<?php echo Text::_('MOD_KUNENALOGIN_LOGIN'); ?>
</button>
</div>
<div class="mod-kunena-login__links">
<?php if ($params->get('showRegister', 1) && $usersConfig->get('allowUserRegistration')) : ?>
<a href="<?php echo Route::_('index.php?option=com_users&view=registration'); ?>"
class="mod-kunena-login__link">
<?php echo Text::_('MOD_KUNENALOGIN_REGISTER'); ?>
<span class="icon-chevron-right" aria-hidden="true"></span>
</a>
<?php endif; ?>
<?php if ($params->get('showForgot', 1)) : ?>
<a href="<?php echo Route::_('index.php?option=com_users&view=reset'); ?>"
class="mod-kunena-login__link">
<?php echo Text::_('MOD_KUNENALOGIN_FORGOT_PASSWORD'); ?>
<span class="icon-chevron-right" aria-hidden="true"></span>
</a>
<?php endif; ?>
</div>
<?php if ($params->get('posttext')) : ?>
<div class="mod-kunena-login__posttext">
<?php echo $params->get('posttext'); ?>
</div>
<?php endif; ?>
<input type="hidden" name="option" value="com_users" />
<input type="hidden" name="task" value="user.login" />
<input type="hidden" name="return" value="<?php echo $return; ?>" />
<?php echo JHtml::_('form.token'); ?>
</form>
<?php endif; ?>
</div>

View File

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

View File

@@ -0,0 +1,74 @@
<?php
/**
* @package Kunena
* @subpackage mod_kunenasearch
*
* @copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*
* Mobile responsive override for mod_kunenasearch module
*/
defined('_JEXEC') or die;
use Joomla\CMS\Language\Text;
$moduleclass_sfx = htmlspecialchars($params->get('moduleclass_sfx', ''), ENT_COMPAT, 'UTF-8');
// Add responsive wrapper class
$wrapperClass = 'mod-kunena-search mod-kunena-search-responsive ' . $moduleclass_sfx;
$button_pos = $params->get('button_pos', 'right');
$button_text = $params->get('button_text', '');
?>
<div class="<?php echo $wrapperClass; ?>">
<form action="<?php echo KunenaRoute::_('index.php?option=com_kunena&view=search'); ?>"
method="post"
class="mod-kunena-search__form mod-kunena-search__form--button-<?php echo $button_pos; ?>">
<?php if ($button_pos === 'top' || $button_pos === 'left') : ?>
<div class="mod-kunena-search__button-wrapper mod-kunena-search__button-wrapper--<?php echo $button_pos; ?>">
<button type="submit" class="mod-kunena-search__button btn btn-primary">
<?php if ($button_text) : ?>
<?php echo htmlspecialchars($button_text, ENT_COMPAT, 'UTF-8'); ?>
<?php else : ?>
<span class="icon-search" aria-hidden="true"></span>
<span class="visually-hidden"><?php echo Text::_('MOD_KUNENASEARCH_SEARCH'); ?></span>
<?php endif; ?>
</button>
</div>
<?php endif; ?>
<div class="mod-kunena-search__input-wrapper">
<label for="mod-kunena-search-<?php echo $module->id; ?>" class="visually-hidden">
<?php echo Text::_('MOD_KUNENASEARCH_SEARCH_FORUM'); ?>
</label>
<input
type="search"
name="q"
id="mod-kunena-search-<?php echo $module->id; ?>"
class="mod-kunena-search__input form-control"
placeholder="<?php echo Text::_('MOD_KUNENASEARCH_SEARCH_FORUM'); ?>"
aria-label="<?php echo Text::_('MOD_KUNENASEARCH_SEARCH_FORUM'); ?>"
value="<?php echo htmlspecialchars($params->get('default_value', ''), ENT_COMPAT, 'UTF-8'); ?>"
/>
</div>
<?php if ($button_pos === 'bottom' || $button_pos === 'right') : ?>
<div class="mod-kunena-search__button-wrapper mod-kunena-search__button-wrapper--<?php echo $button_pos; ?>">
<button type="submit" class="mod-kunena-search__button btn btn-primary">
<?php if ($button_text) : ?>
<?php echo htmlspecialchars($button_text, ENT_COMPAT, 'UTF-8'); ?>
<?php else : ?>
<span class="icon-search" aria-hidden="true"></span>
<span class="visually-hidden"><?php echo Text::_('MOD_KUNENASEARCH_SEARCH'); ?></span>
<?php endif; ?>
</button>
</div>
<?php endif; ?>
<input type="hidden" name="task" value="results" />
<input type="hidden" name="option" value="com_kunena" />
</form>
</div>

View File

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

View File

@@ -0,0 +1,100 @@
<?php
/**
* @package Kunena
* @subpackage mod_kunenastats
*
* @copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*
* Mobile responsive override for mod_kunenastats module
*/
defined('_JEXEC') or die;
use Joomla\CMS\Language\Text;
$moduleclass_sfx = htmlspecialchars($params->get('moduleclass_sfx', ''), ENT_COMPAT, 'UTF-8');
// Add responsive wrapper class
$wrapperClass = 'mod-kunena-stats mod-kunena-stats-responsive ' . $moduleclass_sfx;
?>
<div class="<?php echo $wrapperClass; ?>">
<div class="mod-kunena-stats__container">
<?php if ($params->get('sh_latestMemberCount', 1)) : ?>
<div class="mod-kunena-stats__stat">
<div class="mod-kunena-stats__icon">
<span class="icon-users" aria-hidden="true"></span>
</div>
<div class="mod-kunena-stats__content">
<div class="mod-kunena-stats__value"><?php echo $kunena_stats->memberCount; ?></div>
<div class="mod-kunena-stats__label"><?php echo Text::_('MOD_KUNENASTATS_MEMBERS'); ?></div>
</div>
</div>
<?php endif; ?>
<?php if ($params->get('sh_latestMember', 1) && !empty($kunena_stats->latestMember)) : ?>
<div class="mod-kunena-stats__stat mod-kunena-stats__stat--latest-member">
<div class="mod-kunena-stats__icon">
<span class="icon-user-plus" aria-hidden="true"></span>
</div>
<div class="mod-kunena-stats__content">
<div class="mod-kunena-stats__label"><?php echo Text::_('MOD_KUNENASTATS_LATEST_MEMBER'); ?></div>
<div class="mod-kunena-stats__value mod-kunena-stats__value--link">
<a href="<?php echo $kunena_stats->latestMember->getURL(); ?>">
<?php echo $kunena_stats->latestMember->getName(); ?>
</a>
</div>
</div>
</div>
<?php endif; ?>
<?php if ($params->get('sh_messageCount', 1)) : ?>
<div class="mod-kunena-stats__stat">
<div class="mod-kunena-stats__icon">
<span class="icon-comments" aria-hidden="true"></span>
</div>
<div class="mod-kunena-stats__content">
<div class="mod-kunena-stats__value"><?php echo $kunena_stats->messageCount; ?></div>
<div class="mod-kunena-stats__label"><?php echo Text::_('MOD_KUNENASTATS_MESSAGES'); ?></div>
</div>
</div>
<?php endif; ?>
<?php if ($params->get('sh_topicCount', 1)) : ?>
<div class="mod-kunena-stats__stat">
<div class="mod-kunena-stats__icon">
<span class="icon-folder-open" aria-hidden="true"></span>
</div>
<div class="mod-kunena-stats__content">
<div class="mod-kunena-stats__value"><?php echo $kunena_stats->topicCount; ?></div>
<div class="mod-kunena-stats__label"><?php echo Text::_('MOD_KUNENASTATS_TOPICS'); ?></div>
</div>
</div>
<?php endif; ?>
<?php if ($params->get('sh_todayTopicCount', 0)) : ?>
<div class="mod-kunena-stats__stat">
<div class="mod-kunena-stats__icon">
<span class="icon-calendar-check" aria-hidden="true"></span>
</div>
<div class="mod-kunena-stats__content">
<div class="mod-kunena-stats__value"><?php echo $kunena_stats->todayTopicCount; ?></div>
<div class="mod-kunena-stats__label"><?php echo Text::_('MOD_KUNENASTATS_TODAY_TOPICS'); ?></div>
</div>
</div>
<?php endif; ?>
<?php if ($params->get('sh_yesterdayTopicCount', 0)) : ?>
<div class="mod-kunena-stats__stat">
<div class="mod-kunena-stats__icon">
<span class="icon-calendar" aria-hidden="true"></span>
</div>
<div class="mod-kunena-stats__content">
<div class="mod-kunena-stats__value"><?php echo $kunena_stats->yesterdayTopicCount; ?></div>
<div class="mod-kunena-stats__label"><?php echo Text::_('MOD_KUNENASTATS_YESTERDAY_TOPICS'); ?></div>
</div>
</div>
<?php endif; ?>
</div>
</div>

View File

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

View File

@@ -0,0 +1,105 @@
<?php
/**
* @package OS Membership Pro
* @subpackage mod_osmembership
*
* @copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*
* Mobile responsive override for mod_osmembership module
*/
defined('_JEXEC') or die;
use Joomla\CMS\Language\Text;
$moduleclass_sfx = htmlspecialchars($params->get('moduleclass_sfx', ''), ENT_COMPAT, 'UTF-8');
// Add responsive wrapper class
$wrapperClass = 'mod-osmembership mod-osmembership-responsive ' . $moduleclass_sfx;
?>
<div class="<?php echo $wrapperClass; ?>">
<?php if (!empty($plans)) : ?>
<div class="mod-osmembership__plans">
<?php foreach ($plans as $plan) : ?>
<div class="mod-osmembership__plan">
<?php if ($params->get('show_plan_image', 1) && !empty($plan->image)) : ?>
<div class="mod-osmembership__plan-image">
<img src="<?php echo $plan->image; ?>"
alt="<?php echo htmlspecialchars($plan->title, ENT_COMPAT, 'UTF-8'); ?>" />
</div>
<?php endif; ?>
<div class="mod-osmembership__plan-content">
<h<?php echo $params->get('header_level', 3); ?> class="mod-osmembership__plan-title">
<?php echo htmlspecialchars($plan->title, ENT_COMPAT, 'UTF-8'); ?>
</h<?php echo $params->get('header_level', 3); ?>>
<?php if ($params->get('show_short_description', 1) && !empty($plan->short_description)) : ?>
<div class="mod-osmembership__plan-description">
<?php echo $plan->short_description; ?>
</div>
<?php endif; ?>
<?php if ($params->get('show_price', 1)) : ?>
<div class="mod-osmembership__plan-pricing">
<?php if ($plan->price > 0) : ?>
<div class="mod-osmembership__price">
<span class="mod-osmembership__currency"><?php echo $config->currency_symbol; ?></span>
<span class="mod-osmembership__amount"><?php echo number_format($plan->price, 2); ?></span>
<?php if ($plan->subscription_length > 0) : ?>
<span class="mod-osmembership__period">
/ <?php echo $plan->subscription_length . ' ' . Text::_('OSM_' . strtoupper($plan->subscription_length_unit)); ?>
</span>
<?php endif; ?>
</div>
<?php else : ?>
<div class="mod-osmembership__price mod-osmembership__price--free">
<?php echo Text::_('OSM_FREE'); ?>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if ($params->get('show_features', 1) && !empty($plan->features)) : ?>
<div class="mod-osmembership__features">
<ul class="mod-osmembership__features-list">
<?php foreach (explode("\n", $plan->features) as $feature) : ?>
<?php if (trim($feature)) : ?>
<li class="mod-osmembership__feature">
<span class="icon-check" aria-hidden="true"></span>
<?php echo htmlspecialchars(trim($feature), ENT_COMPAT, 'UTF-8'); ?>
</li>
<?php endif; ?>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<div class="mod-osmembership__actions">
<a href="<?php echo $plan->link; ?>"
class="mod-osmembership__btn btn btn-primary">
<?php echo $params->get('button_text', Text::_('OSM_SUBSCRIBE')); ?>
<span class="icon-chevron-right" aria-hidden="true"></span>
</a>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php if ($params->get('show_all_plans_link', 1)) : ?>
<div class="mod-osmembership__all-plans">
<a href="<?php echo OSMembershipHelperRoute::getCategoriesRoute(); ?>"
class="mod-osmembership__all-plans-link btn btn-secondary">
<?php echo Text::_('OSM_VIEW_ALL_PLANS'); ?>
</a>
</div>
<?php endif; ?>
<?php else : ?>
<div class="mod-osmembership__empty">
<p><?php echo Text::_('OSM_NO_PLANS_AVAILABLE'); ?></p>
</div>
<?php endif; ?>
</div>

View File

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