feat: guided tours framework + DevTools reset tour toggle
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
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
- 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
This commit is contained in:
+190
-65
@@ -108,6 +108,9 @@ class Pkg_MokosuiteclientInstallerScript
|
||||
// Set up MokoSuiteClient guided tours and unpublish Joomla defaults
|
||||
$this->setupGuidedTours();
|
||||
|
||||
// Register MokoSuiteClient guided tour content (tours + steps)
|
||||
$this->registerGuidedTours();
|
||||
|
||||
// Clean up orphaned empty-element rows and stale files from old DEFAULT '' bug
|
||||
$this->cleanupEmptyElements();
|
||||
|
||||
@@ -1486,97 +1489,217 @@ class Pkg_MokosuiteclientInstallerScript
|
||||
);
|
||||
$db->execute();
|
||||
|
||||
// Define MokoSuiteClient tours
|
||||
// Remove old-format tours (superseded by com_mokosuiteclient.* UIDs)
|
||||
$oldUids = [
|
||||
$db->quote('mokosuiteclient-welcome'),
|
||||
$db->quote('mokosuiteclient-firewall'),
|
||||
$db->quote('mokosuiteclient-extensions'),
|
||||
];
|
||||
|
||||
// Delete orphaned steps first
|
||||
$subQuery = $db->getQuery(true)
|
||||
->select($db->quoteName('id'))
|
||||
->from($db->quoteName('#__guidedtours'))
|
||||
->where($db->quoteName('uid') . ' IN (' . implode(',', $oldUids) . ')');
|
||||
$db->setQuery($subQuery);
|
||||
$oldTourIds = $db->loadColumn();
|
||||
|
||||
if (!empty($oldTourIds))
|
||||
{
|
||||
$db->setQuery(
|
||||
$db->getQuery(true)
|
||||
->delete($db->quoteName('#__guidedtour_steps'))
|
||||
->where($db->quoteName('tour_id') . ' IN (' . implode(',', array_map('intval', $oldTourIds)) . ')')
|
||||
)->execute();
|
||||
|
||||
$db->setQuery(
|
||||
$db->getQuery(true)
|
||||
->delete($db->quoteName('#__guidedtours'))
|
||||
->where($db->quoteName('uid') . ' IN (' . implode(',', $oldUids) . ')')
|
||||
)->execute();
|
||||
}
|
||||
|
||||
// Tour registration is now handled by registerGuidedTours()
|
||||
}
|
||||
catch (\Throwable $e)
|
||||
{
|
||||
Log::add('Guided tours setup error: ' . $e->getMessage(), Log::WARNING, 'mokosuiteclient');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register MokoSuiteClient guided tours and their steps.
|
||||
*
|
||||
* Inserts tour definitions into #__guidedtours and step definitions into
|
||||
* #__guidedtour_steps. Skips if the tables do not exist (pre-Joomla 4.3)
|
||||
* or if a tour with the same uid already exists.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 02.47.09
|
||||
*/
|
||||
private function registerGuidedTours(): void
|
||||
{
|
||||
try
|
||||
{
|
||||
$db = Factory::getDbo();
|
||||
|
||||
// Check if #__guidedtours table exists (Joomla 4.3+)
|
||||
$tables = $db->getTableList();
|
||||
$prefix = $db->getPrefix();
|
||||
|
||||
if (!\in_array($prefix . 'guidedtours', $tables, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$now = date('Y-m-d H:i:s');
|
||||
|
||||
// Define tours
|
||||
$tours = [
|
||||
[
|
||||
'uid' => 'mokosuiteclient-welcome',
|
||||
'title' => 'Welcome to MokoSuiteClient',
|
||||
'desc' => 'Get started with the MokoSuiteClient Admin Tools Suite. This tour shows you the key areas of your admin dashboard.',
|
||||
'url' => 'administrator/index.php?option=com_mokosuiteclient',
|
||||
'steps' => [
|
||||
['title' => 'MokoSuiteClient Dashboard', 'desc' => 'This is your MokoSuiteClient control center. You can see site info, feature plugins, WAF activity, and quick actions all in one place.', 'target' => '#mokosuiteclient-dashboard', 'type' => 0],
|
||||
['title' => 'Site Information', 'desc' => 'The info bar shows your Joomla version, PHP version, database type, and debug/offline status at a glance.', 'target' => '.mokosuiteclient-info-bar', 'type' => 0],
|
||||
['title' => 'Quick Actions', 'desc' => 'Use these buttons to clear cache, check updates, manage extensions, and perform common admin tasks with one click.', 'target' => '#mokosuiteclient-btn-cache', 'type' => 0],
|
||||
['title' => 'Feature Plugins', 'desc' => 'MokoSuiteClient features are split into toggleable plugins. Enable or disable security, tenant restrictions, developer tools, and more from here.', 'target' => '.mokosuiteclient-plugin-grid', 'type' => 0],
|
||||
['title' => 'MokoSuiteClient Menu', 'desc' => 'The MokoSuiteClient sidebar menu gives you quick access to all admin tools — Helpdesk, Extensions, WAF Log, Database Tools, and more.', 'target' => '.mokosuiteclient-admin-menu, [class*="mokosuiteclient"]', 'type' => 0],
|
||||
'uid' => 'com_mokosuiteclient.welcome',
|
||||
'title' => 'MokoSuite Welcome',
|
||||
'description' => 'Get started with MokoSuite — configure your health token, send your first heartbeat, and set up trusted IPs.',
|
||||
'extensions' => '["com_mokosuiteclient"]',
|
||||
'url' => 'administrator/index.php?option=com_mokosuiteclient',
|
||||
'steps' => [
|
||||
[
|
||||
'title' => 'Welcome to MokoSuite',
|
||||
'description' => 'This is your MokoSuite control panel. Let\'s walk through the key features.',
|
||||
'target' => '#mokosuiteclient-dashboard',
|
||||
'type' => 2,
|
||||
'position' => 'bottom',
|
||||
],
|
||||
[
|
||||
'title' => 'Site Info Bar',
|
||||
'description' => 'Your site name, version, support PIN, and system info at a glance.',
|
||||
'target' => '.card.mb-4:first-child',
|
||||
'type' => 2,
|
||||
'position' => 'bottom',
|
||||
],
|
||||
[
|
||||
'title' => 'Quick Actions',
|
||||
'description' => 'Clear cache, check updates, manage extensions, and more.',
|
||||
'target' => '#mokosuiteclient-btn-cache',
|
||||
'type' => 2,
|
||||
'position' => 'right',
|
||||
],
|
||||
[
|
||||
'title' => 'Plugin Cards',
|
||||
'description' => 'Enable, disable, and configure MokoSuite plugins from here.',
|
||||
'target' => '.mokosuiteclient-plugin-card:first-child',
|
||||
'type' => 2,
|
||||
'position' => 'top',
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'uid' => 'mokosuiteclient-firewall',
|
||||
'title' => 'MokoSuiteClient Firewall Setup',
|
||||
'desc' => 'Configure the Web Application Firewall to protect your site from common attacks.',
|
||||
'url' => 'administrator/index.php?option=com_plugins&task=plugin.edit&filter[search]=mokosuiteclient_firewall',
|
||||
'steps' => [
|
||||
['title' => 'Firewall Plugin', 'desc' => 'The MokoSuiteClient Firewall provides 10 security shields including SQL injection, XSS, and malicious user agent detection.', 'target' => '', 'type' => 0],
|
||||
['title' => 'WAF Shields', 'desc' => 'Enable or disable individual WAF shields. Each shield protects against a specific attack vector. All shields are enabled by default.', 'target' => '', 'type' => 0],
|
||||
['title' => 'Security Headers', 'desc' => 'Configure HTTP security headers like X-Frame-Options, Content-Security-Policy, and HSTS to harden your site against browser-based attacks.', 'target' => '', 'type' => 0],
|
||||
['title' => 'IP Blocklist', 'desc' => 'Block specific IP addresses, CIDR ranges, or wildcard patterns. The auto-ban feature automatically blocks IPs that trigger too many WAF alerts.', 'target' => '', 'type' => 0],
|
||||
],
|
||||
],
|
||||
[
|
||||
'uid' => 'mokosuiteclient-extensions',
|
||||
'title' => 'Moko Extensions Manager',
|
||||
'desc' => 'Browse and install Moko Consulting extensions from the built-in catalog.',
|
||||
'url' => 'administrator/index.php?option=com_mokosuiteclient&view=extensions',
|
||||
'steps' => [
|
||||
['title' => 'Extension Catalog', 'desc' => 'Browse all available Moko Consulting extensions. Each card shows the extension name, description, install status, and current version.', 'target' => '', 'type' => 0],
|
||||
['title' => 'Install Extensions', 'desc' => 'Click Install to add an extension from the Moko Consulting repository. Updates are handled through Joomla\'s standard update system.', 'target' => '', 'type' => 0],
|
||||
'uid' => 'com_mokosuiteclient.firewall',
|
||||
'title' => 'MokoSuite Firewall Setup',
|
||||
'description' => 'Configure your Web Application Firewall — trusted IPs, WAF shields, and security headers.',
|
||||
'extensions' => '["com_mokosuiteclient"]',
|
||||
'url' => 'administrator/index.php?option=com_plugins&task=plugin.edit&extension_id=0',
|
||||
'steps' => [
|
||||
[
|
||||
'title' => 'Your Current IP',
|
||||
'description' => 'This shows your IP address. Copy it to add to the Trusted IPs list.',
|
||||
'target' => '#mokosuiteclient-current-ip',
|
||||
'type' => 2,
|
||||
'position' => 'bottom',
|
||||
],
|
||||
[
|
||||
'title' => 'Trusted IPs',
|
||||
'description' => 'Add IPs that should bypass WAF checks — your office, VPN, etc.',
|
||||
'target' => '#jform_params_trusted_ips',
|
||||
'type' => 2,
|
||||
'position' => 'top',
|
||||
],
|
||||
[
|
||||
'title' => 'WAF Shields',
|
||||
'description' => 'Enable protection against SQL injection, XSS, malicious agents, and more.',
|
||||
'target' => '#attrib-waf',
|
||||
'type' => 2,
|
||||
'position' => 'bottom',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($tours as $tourDef)
|
||||
{
|
||||
// Check if tour already exists
|
||||
$db->setQuery(
|
||||
$db->getQuery(true)
|
||||
->select('id')
|
||||
->from($db->quoteName('#__guidedtours'))
|
||||
->where($db->quoteName('uid') . ' = ' . $db->quote($tourDef['uid']))
|
||||
);
|
||||
// Check if tour already exists by uid
|
||||
$query = $db->getQuery(true)
|
||||
->select($db->quoteName('id'))
|
||||
->from($db->quoteName('#__guidedtours'))
|
||||
->where($db->quoteName('uid') . ' = ' . $db->quote($tourDef['uid']));
|
||||
$db->setQuery($query);
|
||||
$existingId = (int) $db->loadResult();
|
||||
|
||||
if ($db->loadResult())
|
||||
if ($existingId)
|
||||
{
|
||||
continue;
|
||||
// Update existing tour metadata
|
||||
$update = $db->getQuery(true)
|
||||
->update($db->quoteName('#__guidedtours'))
|
||||
->set($db->quoteName('title') . ' = ' . $db->quote($tourDef['title']))
|
||||
->set($db->quoteName('description') . ' = ' . $db->quote($tourDef['description']))
|
||||
->set($db->quoteName('extensions') . ' = ' . $db->quote($tourDef['extensions']))
|
||||
->set($db->quoteName('url') . ' = ' . $db->quote($tourDef['url']))
|
||||
->set($db->quoteName('published') . ' = 1')
|
||||
->set($db->quoteName('modified') . ' = ' . $db->quote($now))
|
||||
->where($db->quoteName('id') . ' = ' . $existingId);
|
||||
$db->setQuery($update)->execute();
|
||||
|
||||
// Delete existing steps so they are re-inserted fresh
|
||||
$delete = $db->getQuery(true)
|
||||
->delete($db->quoteName('#__guidedtour_steps'))
|
||||
->where($db->quoteName('tour_id') . ' = ' . $existingId);
|
||||
$db->setQuery($delete)->execute();
|
||||
|
||||
$tourId = $existingId;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Insert new tour
|
||||
$tour = (object) [
|
||||
'title' => $tourDef['title'],
|
||||
'uid' => $tourDef['uid'],
|
||||
'description' => $tourDef['description'],
|
||||
'extensions' => $tourDef['extensions'],
|
||||
'url' => $tourDef['url'],
|
||||
'created' => $now,
|
||||
'created_by' => 0,
|
||||
'modified' => $now,
|
||||
'modified_by' => 0,
|
||||
'published' => 1,
|
||||
'language' => '*',
|
||||
'note' => 'MokoSuiteClient',
|
||||
'access' => 1,
|
||||
'ordering' => 0,
|
||||
'autostart' => 0,
|
||||
];
|
||||
|
||||
$db->insertObject('#__guidedtours', $tour, 'id');
|
||||
$tourId = (int) $tour->id;
|
||||
}
|
||||
|
||||
$tour = (object) [
|
||||
'title' => $tourDef['title'],
|
||||
'uid' => $tourDef['uid'],
|
||||
'description' => $tourDef['desc'],
|
||||
'extensions' => '',
|
||||
'url' => $tourDef['url'],
|
||||
'created' => date('Y-m-d H:i:s'),
|
||||
'created_by' => 0,
|
||||
'modified' => date('Y-m-d H:i:s'),
|
||||
'modified_by' => 0,
|
||||
'published' => 1,
|
||||
'language' => '*',
|
||||
'note' => 'MokoSuiteClient',
|
||||
'access' => 3,
|
||||
'ordering' => 0,
|
||||
'autostart' => 0,
|
||||
];
|
||||
|
||||
$db->insertObject('#__guidedtours', $tour, 'id');
|
||||
$tourId = (int) $tour->id;
|
||||
|
||||
// Insert steps
|
||||
foreach ($tourDef['steps'] as $i => $stepDef)
|
||||
{
|
||||
$step = (object) [
|
||||
'tour_id' => $tourId,
|
||||
'title' => $stepDef['title'],
|
||||
'description' => $stepDef['desc'],
|
||||
'description' => $stepDef['description'],
|
||||
'target' => $stepDef['target'],
|
||||
'type' => $stepDef['type'],
|
||||
'interactive_type' => 1,
|
||||
'url' => '',
|
||||
'position' => 'bottom',
|
||||
'position' => $stepDef['position'],
|
||||
'ordering' => $i + 1,
|
||||
'published' => 1,
|
||||
'created' => date('Y-m-d H:i:s'),
|
||||
'created' => $now,
|
||||
'created_by' => 0,
|
||||
'modified' => date('Y-m-d H:i:s'),
|
||||
'modified' => $now,
|
||||
'modified_by' => 0,
|
||||
'language' => '*',
|
||||
'note' => '',
|
||||
@@ -1586,10 +1709,12 @@ class Pkg_MokosuiteclientInstallerScript
|
||||
$db->insertObject('#__guidedtour_steps', $step, 'id');
|
||||
}
|
||||
}
|
||||
|
||||
Log::add('Registered ' . \count($tours) . ' MokoSuiteClient guided tours.', Log::INFO, 'mokosuiteclient');
|
||||
}
|
||||
catch (\Throwable $e)
|
||||
{
|
||||
Log::add('Guided tours setup error: ' . $e->getMessage(), Log::WARNING, 'mokosuiteclient');
|
||||
Log::add('Guided tour registration error: ' . $e->getMessage(), Log::WARNING, 'mokosuiteclient');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user