6c668dae20
- togglePublished: return JSON on CSRF failure instead of die() - Conditions view: fold group/rule counts into main query as subselects instead of N+1 per-item queries - All 5 toggle-published templates: add .catch() for AJAX error feedback Claude-Session: https://claude.ai/code/session_01Jo2JpjCwfHAh2HHRSjczKq
153 lines
6.1 KiB
PHP
153 lines
6.1 KiB
PHP
<?php
|
|
/**
|
|
* @package MokoSuiteClient
|
|
* @subpackage com_mokosuiteclient
|
|
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
|
|
* @license GNU General Public License version 3 or later; see LICENSE
|
|
*
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
*/
|
|
|
|
defined('_JEXEC') or die;
|
|
|
|
use Joomla\CMS\Factory;
|
|
use Joomla\CMS\Router\Route;
|
|
use Joomla\CMS\Session\Session;
|
|
|
|
$items = $this->items;
|
|
$total = $this->total;
|
|
$filters = $this->filters;
|
|
$token = Session::getFormToken();
|
|
$input = Factory::getApplication()->getInput();
|
|
$page = max(1, $input->getInt('page', 1));
|
|
$pages = max(1, ceil($total / 50));
|
|
|
|
$publishedLabels = [1 => 'Published', 0 => 'Unpublished', -2 => 'Trashed'];
|
|
$publishedColors = [1 => 'success', 0 => 'danger', -2 => 'dark'];
|
|
?>
|
|
|
|
<div id="mokosuiteclient-modules">
|
|
<form method="get" action="<?php echo Route::_('index.php?option=com_mokosuiteclient&view=modules'); ?>" class="mb-3">
|
|
<input type="hidden" name="option" value="com_mokosuiteclient">
|
|
<input type="hidden" name="view" value="modules">
|
|
<div class="row g-2 align-items-end">
|
|
<div class="col-md-3">
|
|
<input type="text" name="filter_search" class="form-control form-control-sm" placeholder="Search title, type, or position..." value="<?php echo htmlspecialchars($filters['search'], ENT_QUOTES, 'UTF-8'); ?>">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<select name="filter_client" class="form-select form-select-sm">
|
|
<option value="">All Clients</option>
|
|
<option value="0"<?php echo $filters['client_id'] === '0' ? ' selected' : ''; ?>>Site</option>
|
|
<option value="1"<?php echo $filters['client_id'] === '1' ? ' selected' : ''; ?>>Administrator</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<select name="filter_published" class="form-select form-select-sm">
|
|
<option value="">All States</option>
|
|
<option value="1"<?php echo $filters['published'] === '1' ? ' selected' : ''; ?>>Published</option>
|
|
<option value="0"<?php echo $filters['published'] === '0' ? ' selected' : ''; ?>>Unpublished</option>
|
|
<option value="-2"<?php echo $filters['published'] === '-2' ? ' selected' : ''; ?>>Trashed</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-auto">
|
|
<button type="submit" class="btn btn-sm btn-primary"><span class="icon-search"></span> Filter</button>
|
|
<a href="<?php echo Route::_('index.php?option=com_mokosuiteclient&view=modules'); ?>" class="btn btn-sm btn-outline-secondary">Reset</a>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
|
|
<div class="card">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<span><span class="icon-cube"></span> Module Manager</span>
|
|
<span class="badge bg-secondary"><?php echo number_format($total); ?> total</span>
|
|
</div>
|
|
<div class="table-responsive">
|
|
<table class="table table-striped table-hover mb-0">
|
|
<thead>
|
|
<tr>
|
|
<th style="width:5%">ID</th>
|
|
<th>Title</th>
|
|
<th>Position</th>
|
|
<th>Type</th>
|
|
<th style="width:8%">Client</th>
|
|
<th style="width:8%">Order</th>
|
|
<th style="width:8%">Status</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php if (empty($items)): ?>
|
|
<tr><td colspan="7" class="text-center text-muted py-4">No modules found.</td></tr>
|
|
<?php else: ?>
|
|
<?php foreach ($items as $item): ?>
|
|
<tr>
|
|
<td><?php echo (int) $item->id; ?></td>
|
|
<td>
|
|
<a href="<?php echo Route::_('index.php?option=com_modules&task=module.edit&id=' . (int) $item->id); ?>">
|
|
<?php echo htmlspecialchars($item->title, ENT_QUOTES, 'UTF-8'); ?>
|
|
</a>
|
|
</td>
|
|
<td><code><?php echo htmlspecialchars($item->position ?: '(none)', ENT_QUOTES, 'UTF-8'); ?></code></td>
|
|
<td><small><?php echo htmlspecialchars($item->module, ENT_QUOTES, 'UTF-8'); ?></small></td>
|
|
<td><span class="badge bg-<?php echo $item->client_id ? 'dark' : 'primary'; ?>"><?php echo $item->client_id ? 'Admin' : 'Site'; ?></span></td>
|
|
<td><?php echo (int) $item->ordering; ?></td>
|
|
<td>
|
|
<?php
|
|
$pub = (int) $item->published;
|
|
$label = $publishedLabels[$pub] ?? 'Unknown';
|
|
$color = $publishedColors[$pub] ?? 'secondary';
|
|
?>
|
|
<a href="#" class="mokosuite-toggle-module badge bg-<?php echo $color; ?>"
|
|
data-id="<?php echo (int) $item->id; ?>"
|
|
data-token="<?php echo $token; ?>">
|
|
<?php echo $label; ?>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<?php if ($pages > 1): ?>
|
|
<nav class="mt-3"><ul class="pagination pagination-sm justify-content-center">
|
|
<?php for ($p = 1; $p <= $pages; $p++): ?>
|
|
<li class="page-item<?php echo $p === $page ? ' active' : ''; ?>">
|
|
<a class="page-link" href="<?php echo Route::_('index.php?option=com_mokosuiteclient&view=modules&page=' . $p
|
|
. ($filters['search'] ? '&filter_search=' . urlencode($filters['search']) : '')
|
|
. ($filters['published'] !== '' ? '&filter_published=' . $filters['published'] : '')
|
|
. ($filters['client_id'] !== '' ? '&filter_client=' . $filters['client_id'] : '')); ?>"><?php echo $p; ?></a>
|
|
</li>
|
|
<?php endfor; ?>
|
|
</ul></nav>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
document.querySelectorAll('.mokosuite-toggle-module').forEach(function(el) {
|
|
el.addEventListener('click', function(e) {
|
|
e.preventDefault();
|
|
var id = this.dataset.id, token = this.dataset.token, badge = this;
|
|
var fd = new FormData();
|
|
fd.append('table', 'modules');
|
|
fd.append('id', id);
|
|
fd.append(token, '1');
|
|
fetch('index.php?option=com_mokosuiteclient&task=display.togglePublished&format=json', {
|
|
method: 'POST', headers: {'X-Requested-With': 'XMLHttpRequest'}, body: fd
|
|
}).then(function(r) { return r.json(); }).then(function(d) {
|
|
if (d.success) {
|
|
var pub = d.published;
|
|
badge.className = 'mokosuite-toggle-module badge bg-' + (pub ? 'success' : 'danger');
|
|
badge.textContent = pub ? 'Published' : 'Unpublished';
|
|
}
|
|
}).catch(function() {
|
|
badge.textContent = 'Error';
|
|
badge.className = 'mokosuite-toggle-module badge bg-warning text-dark';
|
|
});
|
|
});
|
|
});
|
|
});
|
|
</script>
|