696e369ec1
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Universal: PR Check / Secret Scan (pull_request) Successful in 10s
Generic: Project CI / Lint & Validate (pull_request) Successful in 17s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 2s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Failing after 48s
Generic: Project CI / Tests (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
- New DashboardModel (BaseDatabaseModel) with getStats(), getCoverageByType(), getMissingArticles() — coverage logic moved out of the list template - New View/Dashboard/HtmlView + tmpl/dashboard/default.php: SVG donut coverage gauge, field-gap badges, per-content_type breakdown table, and a list of published articles missing OG tags (linking to the article editor) with a Batch Generate shortcut - DisplayController default_view -> dashboard; Dashboard + Tags submenu entries - Removed the inline coverage.php include from the tags list (it ran 6 uncached COUNT queries on every list load); that logic now lives in DashboardModel - Declared tmpl/dashboard in the manifest; added language strings (en-GB, en-US)
143 lines
7.6 KiB
PHP
143 lines
7.6 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @package MokoSuiteOpenGraph
|
|
* @subpackage com_mokoog
|
|
* @author Moko Consulting <hello@mokoconsulting.tech>
|
|
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
|
|
* @license GNU General Public License version 3 or later; see LICENSE
|
|
*/
|
|
|
|
defined('_JEXEC') or die;
|
|
|
|
use Joomla\CMS\Language\Text;
|
|
use Joomla\CMS\Router\Route;
|
|
use Joomla\CMS\Uri\Uri;
|
|
|
|
/** @var \Joomla\Component\MokoOG\Administrator\View\Dashboard\HtmlView $this */
|
|
|
|
$s = $this->stats;
|
|
$coverage = (int) ($s['coverage'] ?? 0);
|
|
$total = (int) ($s['total'] ?? 0);
|
|
$withOg = (int) ($s['with_og'] ?? 0);
|
|
|
|
$colorClass = $coverage >= 80 ? 'text-success' : ($coverage >= 50 ? 'text-warning' : 'text-danger');
|
|
$stroke = $coverage >= 80 ? '#198754' : ($coverage >= 50 ? '#ffc107' : '#dc3545');
|
|
|
|
$r = 54.0;
|
|
$circ = 2 * M_PI * $r;
|
|
$dash = round($circ * $coverage / 100, 2);
|
|
$gap = round($circ - $dash, 2);
|
|
?>
|
|
<div class="p-3">
|
|
<div class="row">
|
|
<!-- Coverage donut -->
|
|
<div class="col-lg-4 mb-3">
|
|
<div class="card h-100">
|
|
<div class="card-body text-center">
|
|
<h4 class="card-title"><?php echo Text::_('COM_MOKOOG_COVERAGE_PERCENT'); ?></h4>
|
|
<svg width="160" height="160" viewBox="0 0 140 140" role="img"
|
|
aria-label="<?php echo $coverage; ?>%" class="<?php echo $colorClass; ?>">
|
|
<circle cx="70" cy="70" r="54" fill="none" stroke="#e9ecef" stroke-width="14"></circle>
|
|
<circle cx="70" cy="70" r="54" fill="none" stroke="<?php echo $stroke; ?>" stroke-width="14"
|
|
stroke-dasharray="<?php echo $dash; ?> <?php echo $gap; ?>"
|
|
stroke-linecap="round" transform="rotate(-90 70 70)"></circle>
|
|
<text x="70" y="80" text-anchor="middle" font-size="30" font-weight="bold" fill="currentColor"><?php echo $coverage; ?>%</text>
|
|
</svg>
|
|
<p class="mt-2 mb-0"><?php echo Text::sprintf('COM_MOKOOG_COVERAGE_ARTICLES', $withOg, $total); ?></p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Missing fields -->
|
|
<div class="col-lg-8 mb-3">
|
|
<div class="card h-100">
|
|
<div class="card-body">
|
|
<h4 class="card-title"><?php echo Text::_('COM_MOKOOG_DASHBOARD_FIELD_GAPS'); ?></h4>
|
|
<ul class="list-group list-group-flush">
|
|
<li class="list-group-item d-flex justify-content-between">
|
|
<span><?php echo Text::_('COM_MOKOOG_HEADING_OG_TITLE'); ?></span>
|
|
<span class="badge bg-<?php echo ($s['missing_title'] ?? 0) ? 'warning text-dark' : 'success'; ?>"><?php echo Text::sprintf('COM_MOKOOG_COVERAGE_MISSING_TITLE', (int) ($s['missing_title'] ?? 0)); ?></span>
|
|
</li>
|
|
<li class="list-group-item d-flex justify-content-between">
|
|
<span><?php echo Text::_('COM_MOKOOG_FIELD_OG_DESCRIPTION'); ?></span>
|
|
<span class="badge bg-<?php echo ($s['missing_description'] ?? 0) ? 'warning text-dark' : 'success'; ?>"><?php echo Text::sprintf('COM_MOKOOG_COVERAGE_MISSING_DESC', (int) ($s['missing_description'] ?? 0)); ?></span>
|
|
</li>
|
|
<li class="list-group-item d-flex justify-content-between">
|
|
<span><?php echo Text::_('COM_MOKOOG_HEADING_IMAGE'); ?></span>
|
|
<span class="badge bg-<?php echo ($s['missing_image'] ?? 0) ? 'warning text-dark' : 'success'; ?>"><?php echo Text::sprintf('COM_MOKOOG_COVERAGE_MISSING_IMAGE', (int) ($s['missing_image'] ?? 0)); ?></span>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<!-- Coverage by content type -->
|
|
<div class="col-lg-6 mb-3">
|
|
<div class="card h-100">
|
|
<div class="card-body">
|
|
<h4 class="card-title"><?php echo Text::_('COM_MOKOOG_DASHBOARD_BY_TYPE'); ?></h4>
|
|
<?php if (empty($this->byType)) : ?>
|
|
<p class="text-muted mb-0"><?php echo Text::_('COM_MOKOOG_NO_TAGS'); ?></p>
|
|
<?php else : ?>
|
|
<table class="table table-sm mb-0">
|
|
<thead>
|
|
<tr>
|
|
<th><?php echo Text::_('COM_MOKOOG_HEADING_CONTENT_TYPE'); ?></th>
|
|
<th class="text-end"><?php echo Text::_('JGRID_HEADING_ID'); ?></th>
|
|
<th class="text-end"><?php echo Text::_('COM_MOKOOG_HEADING_OG_TITLE'); ?></th>
|
|
<th class="text-end"><?php echo Text::_('COM_MOKOOG_HEADING_IMAGE'); ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($this->byType as $row) : ?>
|
|
<tr>
|
|
<td><?php echo $this->escape($row->content_type); ?></td>
|
|
<td class="text-end"><?php echo (int) $row->total; ?></td>
|
|
<td class="text-end"><?php echo (int) $row->with_title; ?></td>
|
|
<td class="text-end"><?php echo (int) $row->with_image; ?></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Articles missing OG tags -->
|
|
<div class="col-lg-6 mb-3">
|
|
<div class="card h-100">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
<h4 class="card-title mb-0"><?php echo Text::_('COM_MOKOOG_DASHBOARD_MISSING'); ?></h4>
|
|
<a class="btn btn-sm btn-primary" href="<?php echo Route::_('index.php?option=com_mokoog&view=tags'); ?>">
|
|
<span class="icon-refresh" aria-hidden="true"></span>
|
|
<?php echo Text::_('COM_MOKOOG_TOOLBAR_BATCH_GENERATE'); ?>
|
|
</a>
|
|
</div>
|
|
<?php if (empty($this->missing)) : ?>
|
|
<p class="text-success mb-0">
|
|
<span class="icon-check" aria-hidden="true"></span>
|
|
<?php echo Text::_('COM_MOKOOG_DASHBOARD_ALL_COVERED'); ?>
|
|
</p>
|
|
<?php else : ?>
|
|
<ul class="list-group list-group-flush">
|
|
<?php foreach ($this->missing as $article) : ?>
|
|
<li class="list-group-item py-1">
|
|
<a href="<?php echo Route::_('index.php?option=com_content&task=article.edit&id=' . (int) $article->id); ?>">
|
|
<?php echo $this->escape($article->title); ?>
|
|
</a>
|
|
</li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
<small class="text-muted d-block mt-2"><?php echo Text::_('COM_MOKOOG_DASHBOARD_MISSING_NOTE'); ?></small>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|