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
- 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)
207 lines
5.6 KiB
PHP
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;
|
|
}
|
|
}
|
|
}
|