Files
MokoSuiteClient/source/packages/plg_system_mokosuiteclient_devtools/src/Extension/DevTools.php
T
Jonathan Miller 57b48520af
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Generic: Project CI / Tests (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (push) Blocked by required conditions
Platform: moko-platform CI / CI Summary (push) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Blocked by required conditions
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Blocked by required conditions
Joomla: Extension CI / PHPStan Analysis (pull_request) Blocked by required conditions
Joomla: Extension CI / Build RC Pre-Release (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: moko-platform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 9s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 9s
Universal: PR Check / Branch Policy (pull_request) Successful in 3s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 19s
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
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 18s
Generic: Project CI / Lint & Validate (pull_request) Successful in 48s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 51s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 54s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 19s
Universal: Auto Version Bump / Version Bump (push) Successful in 14s
feat: guided tours framework + DevTools reset tour toggle
- registerGuidedTours() in script.php postflight registers Welcome and
  Firewall tours via Joomla's #__guidedtours/#__guidedtour_steps tables
- Tours use com_mokosuiteclient.* UIDs, auto-update on package update
- DevTools: reset_tour_prompts one-shot toggle clears all guided tour
  completion flags from #__user_profiles on save
- Language keys added for tour reset field
2026-06-23 11:59:48 -05:00

222 lines
5.6 KiB
PHP

<?php
/**
* @package MokoSuiteClient
* @subpackage plg_system_mokosuiteclient_devtools
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
* @license GNU General Public License version 3 or later; see LICENSE
*/
namespace Moko\Plugin\System\MokoSuiteClientDevTools\Extension;
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Log\Log;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\SubscriberInterface;
/**
* MokoSuiteClient Developer Tools Plugin
*
* Provides development mode (disables caching, enables debug), hit counter
* reset, and content version cleanup.
*
* @since 02.32.00
*/
class DevTools extends CMSPlugin implements SubscriberInterface
{
protected $autoloadLanguage = true;
public static function getSubscribedEvents(): array
{
return [
'onAfterInitialise' => 'onAfterInitialise',
'onExtensionAfterSave' => 'onExtensionAfterSave',
];
}
/**
* Apply dev mode settings at runtime.
*/
public function onAfterInitialise(): void
{
if (!$this->params->get('dev_mode', 0))
{
return;
}
$config = Factory::getConfig();
$config->set('caching', 0);
$config->set('debug', 1);
// Show offline page on primary domain
$primaryDomain = $this->params->get('primary_domain', '');
$currentHost = $_SERVER['HTTP_HOST'] ?? '';
if (!empty($primaryDomain) && $currentHost === $primaryDomain)
{
$config->set('offline', 1);
}
// Suppress hit recording via com_content's native parameter
try
{
$contentParams = \Joomla\CMS\Component\ComponentHelper::getParams('com_content');
$contentParams->set('record_hits', 0);
}
catch (\Throwable $e) {}
}
/**
* Handle maintenance actions when this plugin's params are saved.
*/
public function onExtensionAfterSave($event): void
{
// Joomla 6: single event object; Joomla 5: individual args
if (is_object($event) && method_exists($event, 'getArgument'))
{
$context = $event->getArgument('context', $event->getArgument(0, ''));
$table = $event->getArgument('subject', $event->getArgument(1, null));
}
else
{
$context = $event;
$table = func_get_arg(1);
}
if ($context !== 'com_plugins.plugin' || !$table)
{
return;
}
// Only process saves to this plugin
if (($table->element ?? '') !== 'mokosuiteclient_devtools' || ($table->folder ?? '') !== 'system')
{
return;
}
$params = new \Joomla\Registry\Registry($table->params ?? '{}');
// Reset hits on save if toggled on
if ($params->get('reset_hits', 0))
{
$this->resetAllHits();
$params->set('reset_hits', 0);
}
// Delete versions on save if toggled on
if ($params->get('delete_versions', 0))
{
$this->deleteAllVersions();
$params->set('delete_versions', 0);
}
// Reset download keys on save if toggled on
if ($params->get('reset_download_keys', 0))
{
$this->resetDownloadKeys();
$params->set('reset_download_keys', 0);
}
// Reset tour prompts on save if toggled on
if ($params->get('reset_tour_prompts', 0))
{
$this->resetTourPrompts();
$params->set('reset_tour_prompts', 0);
}
// Reset the one-shot toggles
if ($table->params !== $params->toString())
{
$db = Factory::getDbo();
$db->setQuery(
$db->getQuery(true)
->update($db->quoteName('#__extensions'))
->set($db->quoteName('params') . ' = ' . $db->quote($params->toString()))
->where($db->quoteName('extension_id') . ' = ' . (int) $table->extension_id)
)->execute();
}
}
private function resetAllHits(): int
{
$db = Factory::getDbo();
$db->setQuery(
$db->getQuery(true)
->update($db->quoteName('#__content'))
->set($db->quoteName('hits') . ' = 0')
->where($db->quoteName('hits') . ' > 0')
)->execute();
$count = $db->getAffectedRows();
$this->getApplication()->enqueueMessage(\sprintf('Reset hits on %d articles.', $count), 'message');
return $count;
}
private function deleteAllVersions(): int
{
$db = Factory::getDbo();
$db->setQuery(
$db->getQuery(true)->delete($db->quoteName('#__history'))
)->execute();
$count = $db->getAffectedRows();
$this->getApplication()->enqueueMessage(\sprintf('Deleted %d version history records.', $count), 'message');
return $count;
}
private function resetTourPrompts(): int
{
$db = Factory::getDbo();
$db->setQuery(
$db->getQuery(true)
->delete($db->quoteName('#__user_profiles'))
->where($db->quoteName('profile_key') . ' LIKE ' . $db->quote('guidedtours.tour%'))
)->execute();
$count = $db->getAffectedRows();
$this->getApplication()->enqueueMessage(\sprintf('Reset %d guided tour completion flags.', $count), 'message');
return $count;
}
private function resetDownloadKeys(): int
{
$db = Factory::getDbo();
// Find update sites that have a dlid in extra_query
$db->setQuery(
$db->getQuery(true)
->select([$db->quoteName('update_site_id'), $db->quoteName('extra_query')])
->from($db->quoteName('#__update_sites'))
->where($db->quoteName('extra_query') . ' LIKE ' . $db->quote('%dlid=%'))
);
$sites = $db->loadObjectList();
$count = 0;
foreach ($sites as $site)
{
// Parse the query string, remove dlid, rebuild
parse_str($site->extra_query, $parsed);
unset($parsed['dlid']);
$newQuery = http_build_query($parsed);
$db->setQuery(
$db->getQuery(true)
->update($db->quoteName('#__update_sites'))
->set($db->quoteName('extra_query') . ' = ' . $db->quote($newQuery))
->where($db->quoteName('update_site_id') . ' = ' . (int) $site->update_site_id)
)->execute();
$count++;
}
$this->getApplication()->enqueueMessage(\sprintf('Cleared download keys from %d update sites.', $count), 'message');
return $count;
}
}