cleanupLegacyExtensions(); $this->enablePlugin('system', 'mokowaas'); $this->enablePlugin('webservices', 'mokowaas'); $this->enablePlugin('task', 'mokowaasdemo'); // Mark MokoWaaS extensions as protected (prevents disable/uninstall at framework level) $this->protectExtensions(); // Trigger heartbeat registration $this->sendHeartbeat(); } /** * Remove legacy/stale extension entries and filesystem remnants. * * The old standalone plugin was named "mokowaasbrand" (plg_system_mokowaasbrand). * After the rewrite into the pkg_mokowaas package, the old entries and files * may linger — especially on sites restored from old backups. * * @return void * * @since 02.21.00 */ private function cleanupLegacyExtensions(): void { try { $db = Factory::getDbo(); // Legacy element names to remove from #__extensions $legacy = [ $db->quote('mokowaasbrand'), $db->quote('plg_system_mokowaasbrand'), ]; // Delete from #__extensions $query = $db->getQuery(true) ->delete($db->quoteName('#__extensions')) ->where($db->quoteName('element') . ' IN (' . implode(',', $legacy) . ')'); $db->setQuery($query); $affected = $db->execute(); $count = $db->getAffectedRows(); // Remove legacy plugin files from the filesystem $legacyDirs = [ JPATH_PLUGINS . '/system/mokowaasbrand', ]; foreach ($legacyDirs as $dir) { if (is_dir($dir)) { $this->rmdirRecursive($dir); } } if ($count > 0) { Factory::getApplication()->enqueueMessage( sprintf('Removed %d legacy MokoWaaS extension(s).', $count), 'message' ); Log::add( sprintf('Cleaned up %d legacy MokoWaaS extension entries', $count), Log::INFO, 'mokowaas' ); } } catch (\Throwable $e) { Log::add('Legacy cleanup error: ' . $e->getMessage(), Log::WARNING, 'jerror'); } } /** * Recursively remove a directory. * * @param string $dir Directory path * * @return void * * @since 02.21.00 */ private function rmdirRecursive(string $dir): void { $items = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::CHILD_FIRST ); foreach ($items as $item) { if ($item->isDir()) { @rmdir($item->getPathname()); } else { @unlink($item->getPathname()); } } @rmdir($dir); } /** * Enable a plugin by group and element. * * @param string $group Plugin group * @param string $element Plugin element name * * @return void * * @since 2.2.0 */ private function enablePlugin(string $group, string $element): void { try { $db = Factory::getDbo(); $query = $db->getQuery(true) ->update($db->quoteName('#__extensions')) ->set($db->quoteName('enabled') . ' = 1') ->where($db->quoteName('type') . ' = ' . $db->quote('plugin')) ->where($db->quoteName('folder') . ' = ' . $db->quote($group)) ->where($db->quoteName('element') . ' = ' . $db->quote($element)); $db->setQuery($query); $db->execute(); } catch (\Throwable $e) { Log::add('Error enabling plugin ' . $group . '/' . $element . ': ' . $e->getMessage(), Log::WARNING, 'jerror'); } } /** * Set the protected flag on all MokoWaaS extensions. * * Joomla's protected flag prevents disabling and uninstalling at the * framework level — no plugin-side interception needed. * * @return void * * @since 02.03.10 */ private function protectExtensions(): void { try { $db = Factory::getDbo(); // All MokoWaaS elements: package, system plugin, component, // webservices plugins, task plugin $elements = [ $db->quote('pkg_mokowaas'), $db->quote('mokowaas'), $db->quote('com_mokowaas'), $db->quote('mokowaasdemo'), $db->quote('perfectpublisher'), ]; $query = $db->getQuery(true) ->update($db->quoteName('#__extensions')) ->set($db->quoteName('protected') . ' = 1') ->set($db->quoteName('locked') . ' = 0') ->where($db->quoteName('element') . ' IN (' . implode(',', $elements) . ')'); $db->setQuery($query); $db->execute(); // Ensure update server stays enabled $this->enableUpdateServer(); } catch (\Throwable $e) { Log::add('Error protecting MokoWaaS extensions: ' . $e->getMessage(), Log::WARNING, 'jerror'); } } /** * Ensure the MokoWaaS update server entry stays enabled. * * Joomla stores update server records in #__update_sites. If a tenant * or automation disables it, the site stops receiving updates. This * re-enables it on every install/update. * * @return void * * @since 02.21.00 */ private function enableUpdateServer(): void { try { $db = Factory::getDbo(); // Find update site by name or URL pattern $query = $db->getQuery(true) ->update($db->quoteName('#__update_sites')) ->set($db->quoteName('enabled') . ' = 1') ->where('(' . $db->quoteName('name') . ' LIKE ' . $db->quote('%MokoWaaS%') . ' OR ' . $db->quoteName('location') . ' LIKE ' . $db->quote('%MokoWaaS%') . ')'); $db->setQuery($query); $db->execute(); } catch (\Throwable $e) { Log::add('Error enabling update server: ' . $e->getMessage(), Log::WARNING, 'jerror'); } } /** * Send heartbeat to the MokoWaaS monitoring receiver. * * @return void * * @since 02.03.08 */ private function sendHeartbeat(): void { try { $db = Factory::getDbo(); $query = $db->getQuery(true) ->select($db->quoteName('params')) ->from($db->quoteName('#__extensions')) ->where($db->quoteName('element') . ' = ' . $db->quote('mokowaas')) ->where($db->quoteName('type') . ' = ' . $db->quote('plugin')) ->where($db->quoteName('folder') . ' = ' . $db->quote('system')); $params = json_decode((string) $db->setQuery($query)->loadResult()); $healthToken = $params->health_api_token ?? ''; if (empty($healthToken)) { return; } $siteUrl = rtrim(\Joomla\CMS\Uri\Uri::root(), '/'); $siteName = Factory::getConfig()->get('sitename', 'Joomla'); $payload = json_encode([ 'site_url' => $siteUrl, 'site_name' => $siteName, 'health_token' => $healthToken, 'action' => 'register', ], JSON_UNESCAPED_SLASHES); $ch = curl_init('https://bench.mokoconsulting.tech/api/waas-heartbeat/register'); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json', 'X-MokoWaaS-Key: moko-waas-hb-2026-x9k4m', ]); curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 15); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); $response = curl_exec($ch); $code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($code >= 200 && $code < 300) { Factory::getApplication()->enqueueMessage('Grafana heartbeat: site registered', 'message'); } } catch (\Throwable $e) { // Silent failure — heartbeat is non-critical } } }