Files
MokoSuiteCross/source/packages/com_mokosuitecross/src/Model/DashboardModel.php
T
Jonathan Miller 27505f7501 fix: rename all MOKOJOOMCROSS language keys and events to MOKOSUITECROSS (#128, #138)
Completes the MokoJoomCross → MokoSuiteCross rebrand across all language
string keys, Joomla event names, documentation, and wiki pages.

- 1,151 language key references renamed (COM_, PLG_, PKG_ prefixes)
- Event names renamed (onMokoJoomCross* → onMokoSuiteCross*)
- CLAUDE.md, CHANGELOG.md, wiki docs updated
- Zero mokojoomcross references remaining in codebase

Closes #128, closes #138
2026-06-21 17:23:02 -05:00

197 lines
6.8 KiB
PHP

<?php
/**
* @package MokoSuiteCross
* @subpackage com_mokosuitecross
* @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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
namespace Joomla\Component\MokoSuiteCross\Administrator\Model;
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Model\BaseDatabaseModel;
class DashboardModel extends BaseDatabaseModel
{
/**
* Get summary statistics for the dashboard.
*
* @return object Stats object with counts
*/
public function getStats(): object
{
$db = $this->getDatabase();
$stats = new \stdClass();
// Active services count
$query = $db->getQuery(true)
->select('COUNT(*)')
->from($db->quoteName('#__mokosuitecross_services'))
->where($db->quoteName('published') . ' = 1');
$db->setQuery($query);
$stats->active_services = (int) $db->loadResult();
// Posts by status
foreach (['queued', 'posted', 'failed'] as $status) {
$query = $db->getQuery(true)
->select('COUNT(*)')
->from($db->quoteName('#__mokosuitecross_posts'))
->where($db->quoteName('status') . ' = ' . $db->quote($status));
$db->setQuery($query);
$stats->{$status . '_count'} = (int) $db->loadResult();
}
return $stats;
}
/**
* Check if Perfect Publisher Pro migration is available.
*
* @return bool
*/
public function isMigrationAvailable(): bool
{
$db = $this->getDatabase();
$query = $db->getQuery(true)
->select($db->quoteName('params'))
->from($db->quoteName('#__extensions'))
->where($db->quoteName('type') . ' = ' . $db->quote('component'))
->where($db->quoteName('element') . ' = ' . $db->quote('com_mokosuitecross'));
$db->setQuery($query);
$params = json_decode($db->loadResult() ?: '{}', true);
return !empty($params['migration_available']);
}
/**
* Get recent activity log entries.
*
* @param int $limit Number of entries to return
*
* @return array
*/
public function getRecentActivity(int $limit = 10): array
{
$db = $this->getDatabase();
$query = $db->getQuery(true)
->select('l.*, s.title AS service_title, s.service_type')
->from($db->quoteName('#__mokosuitecross_logs', 'l'))
->join('LEFT', $db->quoteName('#__mokosuitecross_services', 's')
. ' ON ' . $db->quoteName('s.id') . ' = ' . $db->quoteName('l.service_id'))
->order($db->quoteName('l.created') . ' DESC');
$db->setQuery($query, 0, $limit);
return $db->loadObjectList() ?: [];
}
/**
* Get posts-per-service breakdown for the analytics chart.
*
* @param string|null $since Only count posts created on or after this datetime
*
* @return array [['service_type' => '...', 'posted' => N, 'failed' => N, 'queued' => N], ...]
*/
public function getServiceBreakdown(?string $since = null): array
{
$db = $this->getDatabase();
$query = $db->getQuery(true)
->select([
$db->quoteName('s.id', 'service_id'),
$db->quoteName('s.service_type'),
$db->quoteName('s.title', 'service_title'),
'SUM(CASE WHEN ' . $db->quoteName('p.status') . ' = ' . $db->quote('posted') . ' THEN 1 ELSE 0 END) AS posted',
'SUM(CASE WHEN ' . $db->quoteName('p.status') . ' = ' . $db->quote('failed') . ' THEN 1 ELSE 0 END) AS failed',
'SUM(CASE WHEN ' . $db->quoteName('p.status') . ' = ' . $db->quote('queued') . ' THEN 1 ELSE 0 END) AS queued',
'COUNT(*) AS total',
])
->from($db->quoteName('#__mokosuitecross_posts', 'p'))
->join('INNER', $db->quoteName('#__mokosuitecross_services', 's')
. ' ON ' . $db->quoteName('s.id') . ' = ' . $db->quoteName('p.service_id'))
->group($db->quoteName(['s.id', 's.service_type', 's.title']))
->order('total DESC');
if ($since !== null) {
$query->where($db->quoteName('p.created') . ' >= ' . $db->quote($since));
}
$db->setQuery($query);
return $db->loadAssocList() ?: [];
}
/**
* Get posts-per-day for the last N days (for trend chart).
*
* @param int $days Number of days to look back
*
* @return array [['day' => '2026-05-28', 'posted' => N, 'failed' => N], ...]
*/
public function getDailyTrend(int $days = 14): array
{
$db = $this->getDatabase();
$cutoff = Factory::getDate('now - ' . $days . ' days')->format('Y-m-d');
$query = $db->getQuery(true)
->select([
'DATE(' . $db->quoteName('created') . ') AS day',
'SUM(CASE WHEN ' . $db->quoteName('status') . ' = ' . $db->quote('posted') . ' THEN 1 ELSE 0 END) AS posted',
'SUM(CASE WHEN ' . $db->quoteName('status') . ' = ' . $db->quote('failed') . ' THEN 1 ELSE 0 END) AS failed',
'COUNT(*) AS total',
])
->from($db->quoteName('#__mokosuitecross_posts'))
->where('DATE(' . $db->quoteName('created') . ') >= ' . $db->quote($cutoff))
->group('DATE(' . $db->quoteName('created') . ')')
->order('day ASC');
$db->setQuery($query);
return $db->loadAssocList() ?: [];
}
/**
* Get most cross-posted articles.
*
* @param int $limit Number of articles
* @param string|null $since Only count posts created on or after this datetime
*
* @return array
*/
public function getTopArticles(int $limit = 5, ?string $since = null): array
{
$db = $this->getDatabase();
$query = $db->getQuery(true)
->select([
$db->quoteName('c.id'),
$db->quoteName('c.title'),
'COUNT(*) AS post_count',
'SUM(CASE WHEN ' . $db->quoteName('p.status') . ' = ' . $db->quote('posted') . ' THEN 1 ELSE 0 END) AS success_count',
])
->from($db->quoteName('#__mokosuitecross_posts', 'p'))
->join('INNER', $db->quoteName('#__content', 'c')
. ' ON ' . $db->quoteName('c.id') . ' = ' . $db->quoteName('p.article_id'))
->group($db->quoteName(['c.id', 'c.title']))
->order('post_count DESC');
if ($since !== null) {
$query->where($db->quoteName('p.created') . ' >= ' . $db->quote($since));
}
$db->setQuery($query, 0, $limit);
return $db->loadAssocList() ?: [];
}
}