| Current Path : /home/u4uwebca/public_html/pano.4uweb.ca/src/Model/ |
| Current File : /home/u4uwebca/public_html/pano.4uweb.ca/src/Model/Main.php |
<?php
/**
* @package panopticon
* @copyright Copyright (c)2023-2024 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license https://www.gnu.org/licenses/agpl-3.0.txt GNU Affero General Public License, version 3 or later
*/
namespace Akeeba\Panopticon\Model;
defined('AKEEBA') || die;
use Akeeba\Panopticon\Container;
use Akeeba\Panopticon\Library\Cache\CallbackController;
use Akeeba\Panopticon\Library\Enumerations\CMSType;
use Akeeba\Panopticon\Library\SoftwareVersions\PhpVersion;
use Awf\Date\Date;
use Awf\Input\Input;
use Awf\Mvc\Model;
/**
* Main Page Model. Used to get the sites overview information.
*
* @since 1.0.0
*/
class Main extends Model
{
public function getBestLayout(Input $input): string
{
$user = $this->getContainer()->userManager->getUser();
$userPreference = $user?->getParameters()?->get('main_layout', 'default');
$urlLayout = $input->get('layout', null);
$storedLayout = $this->getContainer()->segment->get('main.layout', null);
// This prevents using layout=cron from getting us stuck on that page :p
if (!in_array($urlLayout, ['default', 'dashboard']))
{
$urlLayout = null;
}
// No layout in the URL, nor stored in the session. Return the user preference.
if ($urlLayout === null && $storedLayout === null)
{
return $userPreference ?? 'default';
}
// No layout in the URL, but a stored layout exists. Return it.
if ($urlLayout === null)
{
return $storedLayout;
}
// A layout is specified in the URL. Store it and return it.
$this->getContainer()->segment->set('main.layout', $urlLayout);
if ($urlLayout === 'default' && $storedLayout !== null && $storedLayout !== 'default')
{
$input->set('limitstart', $input->getInt('limitstart', 0));
}
return $urlLayout;
}
public function getHighestJoomlaVersion(): ?string
{
$db = $this->container->db;
$query = $db->getQuery(true);
$query->selectDistinct(
$query->jsonExtract($db->quoteName('config'), '$.core.latest.version')
)->from($db->quoteName('#__sites'));
$versions = $db->setQuery($query)->loadColumn(0) ?: [];
return array_reduce(
$versions,
fn(?string $carry, string $item) => $carry === null
? $item
: (version_compare($carry, $item, 'lt') ? $item : $carry),
null
);
}
public function getLastCronExecutionTime(): ?Date
{
$db = $this->container->db;
$db->lockTable('#__akeeba_common');
$query = $db->getQuery(true)
->select($db->quoteName('value'))
->from($db->quoteName('#__akeeba_common'))
->where($db->quoteName('key') . ' = ' . $db->quote('panopticon.task.last.execution'));
$lastExecution = $db->setQuery($query)->loadResult();
$db->unlockTables();
if (empty($lastExecution))
{
return null;
}
try
{
return $this->container->dateFactory($lastExecution);
}
catch (\Throwable $e)
{
return null;
}
}
/**
* Are the CRON jobs working?
*
* We consider them to be working when there is a CRON job which has executed within the last minute.
*
* @return bool
* @since 1.0.5
*/
public function areCRONJobsWorking(): bool
{
$lastExecutionTime = $this->getLastCronExecutionTime();
if (empty($lastExecutionTime))
{
return false;
}
$lastPlausibleRun = ($this->container->dateFactory())->sub(new \DateInterval('PT55S'));
$lastPlausibleRun->setTime($lastPlausibleRun->hour, $lastPlausibleRun->minute, 0, 0);
return $lastPlausibleRun->diff($lastExecutionTime)->invert === 0;
}
/**
* How far behind is the execution of CRON jobs, on average?
*
* @param int $threshold The minimum number of seconds to report.
*
* @return int|null NULL if CRON jobs are not working
* @since 1.0.5
*/
public function getCRONJobsSecondsBehind(int $threshold = 120): ?int
{
if (!$this->areCRONJobsWorking())
{
return null;
}
$now = time();
$db = $this->getContainer()->db;
$query = $db->getQuery(true)
->select($db->quoteName('next_execution'))
->from('#__tasks')
->where(
[
$db->quoteName('enabled') . ' = 1',
$db->quoteName('next_execution') . ' < UNIX_TIMESTAMP()',
]
);
$timestamps = $db->setQuery($query)->loadColumn();
if (empty($timestamps))
{
return 0;
}
$timestamps = array_map(
fn(string $stamp) => $now - (new \DateTime($stamp))->format('U'),
$timestamps
);
$averageDelay = (int) ceil(array_sum($timestamps) / count($timestamps));
if ($averageDelay < $threshold)
{
return 0;
}
return $averageDelay;
}
public function getKnownCMSVersions(?string $siteType = null): array
{
/** @var Container $container */
$container = $this->container;
$cacheController = new CallbackController(
container: $container,
pool: $container->cacheFactory->pool('system'),
);
return $cacheController->get(
callback: function () use ($siteType): array {
$db = $this->container->db;
$query = $db->getQuery(true);
$query
->select(
[
'DISTINCT SUBSTR(SUBSTRING_INDEX(' .
$query->jsonExtract($db->quoteName('config'), '$.core.current.version') .
', ' . $db->quote('.') . ', 2) FROM 2) AS ' .
$db->quoteName('version'),
'IFNULL(' .
$query->jsonExtract($db->qn('config'), '$.cmsType') .
',' .
$db->quote(CMSType::JOOMLA->value) .
') AS ' . $db->quoteName('cmsType')
]
)
->from($db->quoteName('#__sites'))
->where($db->quoteName('enabled') . ' = 1')
->order($db->quoteName('version') . ' DESC');
if ($siteType === CMSType::JOOMLA->value)
{
$query->where(
'(' .
$query->jsonExtract($db->quoteName('config'), '$.cmsType') . ' = ' . $db->quote(CMSType::JOOMLA->value) .
' OR ' .
$query->jsonExtract($db->quoteName('config'), '$.cmsType') . ' IS NULL' .
')'
);
}
elseif (!empty($siteType))
{
$query->where(
$query->jsonExtract($db->quoteName('config'), '$.cmsType') . ' = ' . $db->quote($siteType)
);
}
$versions = $db->setQuery($query)->loadObjectList();
if (empty($versions))
{
return [];
}
// Remove sites which have failed to load.
$versions = array_filter($versions);
// Separate per CMS
$temp = [];
foreach ($versions as $versionObject) {
$type = trim($versionObject->cmsType, '"');
$temp[$type] ??= [];
$temp[$type][] = $versionObject->version;
}
$types = array_keys($temp);
foreach ($types as $type)
{
uasort($temp[$type], function ($a, $b) {
version_compare($a, $b);
});
}
asort($types);
$versions = [];
foreach ($types as $type)
{
foreach ($temp[$type] as $item)
{
$versions[$type . '.' . $item] = (CMSType::tryFrom($type)?->forHumans() ?? '(???)') . ' ' . $item;
}
}
return $versions;
},
id: 'known_' . ($siteType ?? 'all_cms') . '_versions',
expiration: 60
);
}
public function getKnownPHPVersions(): array
{
$phpVersion = new PhpVersion($this->container);
$versions = array_keys($phpVersion->getPhpEolInformation());
return array_combine($versions, $versions);
}
public function getSiteNamesForSelect(bool $enabled = true, ?string $emptyLabel = null): array
{
$db = $this->getContainer()->db;
$query = $db->getQuery(true)
->select(
[
$db->quoteName('id'),
$db->quoteName('name'),
]
)
->from($db->quoteName('#__sites'))
->order($db->quoteName('name') . ' ASC, ' . $db->quoteName('id') . ' ASC');
$ret = $db->setQuery($query)->loadAssocList('id', 'name') ?: [];
if (!empty($emptyLabel))
{
$ret = array_combine(
array_merge([''], array_keys($ret)),
array_merge([$this->getLanguage()->text($emptyLabel)], array_values($ret))
);
}
return $ret;
}
}