Files
MokoSuiteClient/source/packages/mod_mokosuiteclient_cpanel/src/Helper/CpanelHelper.php
T
Jonathan Miller d7efb61207
Universal: PR Check / Branch Policy (pull_request) Successful in 4s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 10s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 17s
Universal: PR Check / Validate PR (pull_request) Failing after 12s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: Auto Version Bump / Version Bump (push) Successful in 20s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 15s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 40s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 14s
Generic: Project CI / Lint & Validate (pull_request) Successful in 41s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 42s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (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
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
fix(cpanel): add missing sitename/db_type to helper, pill button group
- CpanelHelper now returns sitename and db_type for info bar
- Cache module renders as single pill with three joined buttons
  (domain key, cache clear, temp clear)
2026-06-23 12:29:53 -05:00

207 lines
5.6 KiB
PHP

<?php
/**
* @package MokoSuiteClient
* @subpackage mod_mokosuiteclient_cpanel
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
* @license GNU General Public License version 3 or later; see LICENSE
*/
namespace Moko\Module\MokoSuiteClientCpanel\Administrator\Helper;
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Version;
use Joomla\Database\DatabaseInterface;
class CpanelHelper
{
/**
* Get basic site info for the cpanel card header.
*/
public function getSiteInfo(DatabaseInterface $db): object
{
$config = Factory::getConfig();
$query = $db->getQuery(true)
->select($db->quoteName('manifest_cache'))
->from($db->quoteName('#__extensions'))
->where($db->quoteName('element') . ' = ' . $db->quote('pkg_mokosuiteclient'))
->where($db->quoteName('type') . ' = ' . $db->quote('package'));
$db->setQuery($query);
$pkgCache = json_decode($db->loadResult() ?? '{}');
return (object) [
'sitename' => $config->get('sitename', ''),
'mokosuiteclient_version' => $pkgCache->version ?? '',
'joomla_version' => (new Version())->getShortVersion(),
'php_version' => PHP_VERSION,
'db_type' => $config->get('dbtype', 'mysql'),
'debug' => (bool) $config->get('debug'),
'offline' => (bool) $config->get('offline'),
];
}
/**
* Get MokoSuiteClient system feature plugins with their enabled state.
*/
public function getFeaturePlugins(DatabaseInterface $db): array
{
$query = $db->getQuery(true)
->select([
$db->quoteName('extension_id'),
$db->quoteName('name'),
$db->quoteName('element'),
$db->quoteName('enabled'),
])
->from($db->quoteName('#__extensions'))
->where($db->quoteName('type') . ' = ' . $db->quote('plugin'))
->where($db->quoteName('folder') . ' = ' . $db->quote('system'))
->where('(' . $db->quoteName('element') . ' = ' . $db->quote('mokosuiteclient')
. ' OR ' . $db->quoteName('element') . ' LIKE ' . $db->quote('mokosuiteclient\\_%') . ')')
->order($db->quoteName('element') . ' ASC');
$db->setQuery($query);
return $db->loadObjectList() ?: [];
}
/**
* Quick database connectivity check.
*/
public function isDatabaseOk(DatabaseInterface $db): bool
{
try
{
$db->setQuery('SELECT 1');
$db->loadResult();
return true;
}
catch (\Throwable $e)
{
return false;
}
}
/**
* Get content and system counts.
*/
public function getCounts(DatabaseInterface $db): object
{
$counts = (object) [
'articles' => 0,
'users' => 0,
'extensions' => 0,
'updates' => 0,
'moko_updates' => 0,
];
try
{
$db->setQuery($db->getQuery(true)->select('COUNT(*)')->from($db->quoteName('#__content')));
$counts->articles = (int) $db->loadResult();
$db->setQuery($db->getQuery(true)->select('COUNT(*)')->from($db->quoteName('#__users')));
$counts->users = (int) $db->loadResult();
$db->setQuery($db->getQuery(true)->select('COUNT(*)')->from($db->quoteName('#__extensions'))->where($db->quoteName('enabled') . ' = 1'));
$counts->extensions = (int) $db->loadResult();
$db->setQuery($db->getQuery(true)->select('COUNT(*)')->from($db->quoteName('#__updates'))->where($db->quoteName('extension_id') . ' != 0'));
$counts->updates = (int) $db->loadResult();
// MokoSuiteClient-specific updates
$db->setQuery(
$db->getQuery(true)
->select('COUNT(*)')
->from($db->quoteName('#__updates', 'u'))
->join('INNER', $db->quoteName('#__extensions', 'e') . ' ON e.extension_id = u.extension_id')
->where('(' . $db->quoteName('e.element') . ' LIKE ' . $db->quote('mokosuiteclient%')
. ' OR ' . $db->quoteName('e.element') . ' LIKE ' . $db->quote('pkg_mokosuiteclient%')
. ' OR ' . $db->quoteName('e.element') . ' LIKE ' . $db->quote('com_mokosuiteclient%')
. ' OR ' . $db->quoteName('e.element') . ' LIKE ' . $db->quote('mod_mokosuiteclient%')
. ' OR ' . $db->quoteName('e.element') . ' = ' . $db->quote('mokoonyx') . ')')
);
$counts->moko_updates = (int) $db->loadResult();
}
catch (\Throwable $e)
{
// Silent
}
return $counts;
}
/**
* Get disk usage info.
*/
public function getDiskInfo(): object
{
$free = @disk_free_space(JPATH_ROOT);
$total = @disk_total_space(JPATH_ROOT);
return (object) [
'free_mb' => $free !== false ? round($free / 1048576) : null,
'total_mb' => $total !== false ? round($total / 1048576) : null,
];
}
/**
* Get the current visitor's IP address.
*/
public function getCurrentIp(): string
{
return $_SERVER['REMOTE_ADDR'] ?? '';
}
/**
* Check SSL certificate expiry (#148).
*
* @return object|null {expires, days_remaining, warning} or null if check fails
*/
public function getSslStatus(): ?object
{
try
{
$host = parse_url(\Joomla\CMS\Uri\Uri::root(), PHP_URL_HOST);
if (empty($host))
{
return null;
}
$context = stream_context_create(['ssl' => ['capture_peer_cert' => true, 'verify_peer' => false]]);
$client = @stream_socket_client('ssl://' . $host . ':443', $errno, $errstr, 5, STREAM_CLIENT_CONNECT, $context);
if (!$client)
{
return null;
}
$params = stream_context_get_params($client);
fclose($client);
$cert = openssl_x509_parse($params['options']['ssl']['peer_certificate'] ?? '');
if (empty($cert['validTo_time_t']))
{
return null;
}
$expires = $cert['validTo_time_t'];
$days = (int) floor(($expires - time()) / 86400);
return (object) [
'expires' => date('Y-m-d', $expires),
'days_remaining' => $days,
'warning' => $days <= 30,
'critical' => $days <= 7,
];
}
catch (\Throwable $e)
{
return null;
}
}
}