. */ namespace Alledia\Installer\OSMap; use Alledia\Framework\Joomla\Extension\Generic; use Joomla\CMS\Factory; defined('_JEXEC') or die(); class XmapConverter { /** * @var array */ protected $xmapPluginsParams = []; /** * List of refactored Xmap plugins to migrate the settings * * @var array */ protected $refactoredXmapPlugins = ['com_content' => 'joomla']; /** * Look for the Xmap data to suggest a data migration * * @return bool True if Xmap data was found */ public function checkXmapDataExists(): bool { $db = Factory::getDbo(); // Do we have any Xmap sitemap? $query = $db->getQuery(true) ->select('COUNT(*)') ->from('#__xmap_sitemap'); $total = (int)$db->setQuery($query)->loadResult(); return $total > 0; } /** * Save the Xmap plugins params into the new plugins. Receives a list of * plugin names to look for params. * * @return void */ public function saveXmapPluginParamsIfExists() { $db = Factory::getDbo(); $query = $db->getQuery(true) ->select([ 'element', 'params' ]) ->from('#__extensions') ->where([ 'type = "plugin"', 'folder = "xmap"', 'element IN ("' . implode('","', array_keys($this->refactoredXmapPlugins)) . '")' ]); $legacyPlugins = $db->setQuery($query)->loadObjectList(); // Check if the respective OSMap plugin is already installed. If so, do not save its params to not override. if ($legacyPlugins) { foreach ($legacyPlugins as $plugin) { $query = $db->getQuery(true) ->select('extension_id') ->from('#__extensions') ->where([ 'type = "plugin"', 'folder = "osmap"', 'element = "' . $plugin->element . '"' ]); $osmapPluginID = $db->setQuery($query)->loadResult(); if (empty($osmapPluginID)) { $this->xmapPluginsParams[] = $plugin; } } } } /** * This method move the Xmap plugins' params to the OSMap plugins. * * @return void */ public function moveXmapPluginsParamsToOSMapPlugins() { $db = Factory::getDbo(); if (!empty($this->xmapPluginsParams)) { foreach ($this->xmapPluginsParams as $plugin) { // Look for the OSMap plugin $query = $db->getQuery(true) ->select('extension_id') ->from('#__extensions') ->where([ 'type = "plugin"', 'folder = "osmap"', 'element = ' . $db->quote($this->refactoredXmapPlugins[$plugin->element] ?? '') ]); $osmapPluginID = $db->setQuery($query)->loadResult(); if (!empty($osmapPluginID)) { $query = $db->getQuery(true) ->update('#__extensions') ->set('params = ' . $db->quote(addslashes($plugin->params))) ->where('extension_id = ' . $osmapPluginID); $db->setQuery($query)->execute(); } } } } /** * Migrates data from Xmap to OSMap. * * @return void */ public function migrateData() { $result = (object)[ 'success' => false ]; $db = Factory::getDbo(); $db->transactionStart(); try { // Do we have any Xmap sitemap? $sitemapFailedIds = []; $itemFailedIds = []; $query = $db->getQuery(true) ->select('*') ->from('#__xmap_sitemap'); $sitemaps = $db->setQuery($query)->loadObjectList(); if ($sitemaps) { // Cleanup the db tables $tables = [ '#__osmap_items_settings', '#__osmap_sitemap_menus', '#__osmap_sitemaps' ]; foreach ($tables as $table) { $db->setQuery( $db->getQuery(true)->delete($db->quoteName($table)) )->execute(); } // Import the sitemaps foreach ($sitemaps as $sitemap) { $query = $db->getQuery(true) ->set([ $db->quoteName('id') . '=' . $db->quote($sitemap->id), $db->quoteName('name') . '=' . $db->quote($sitemap->title), $db->quoteName('is_default') . '=' . $db->quote($sitemap->is_default), $db->quoteName('published') . '=' . $db->quote($sitemap->state), $db->quoteName('created_on') . '=' . $db->quote($sitemap->created) ]) ->insert('#__osmap_sitemaps'); if ($db->setQuery($query)->execute()) { // Add the selected menus to the correct table if ($menus = json_decode($sitemap->selections, true)) { foreach ($menus as $menuType => $menu) { if ($menuTypeId = $this->getMenuTypeId($menuType)) { // Convert the selection of menus into a row $query = $db->getQuery(true) ->insert('#__osmap_sitemap_menus') ->columns([ 'sitemap_id', 'menutype_id', 'priority', 'changefreq', 'ordering' ]) ->values( implode( ',', [ $db->quote($sitemap->id), $db->quote($menuTypeId), $db->quote($menu['priority']), $db->quote($menu['changefreq']), $db->quote($menu['ordering']) ] ) ); $db->setQuery($query)->execute(); } } } // Convert settings about excluded items if ($sitemap->excluded_items ?? null) { if ($excludedItems = json_decode($sitemap->excluded_items, true)) { foreach ($excludedItems as $item) { $uid = $this->convertItemUID($item[0]); // Check if the item was already registered $query = $db->getQuery(true) ->select('COUNT(*)') ->from('#__osmap_items_settings') ->where([ 'sitemap_id = ' . $db->quote($sitemap->id), 'uid = ' . $db->quote($uid) ]); $count = $db->setQuery($query)->loadResult(); if ($count == 0) { // Insert the settings $query = $db->getQuery(true) ->insert('#__osmap_items_settings') ->columns([ 'sitemap_id', 'uid', 'published', 'changefreq', 'priority' ]) ->values( implode( ',', [ $sitemap->id, $db->quote($uid), 0, $db->quote('weekly'), $db->quote('0.5') ] ) ); } else { // Update the setting $query = $db->getQuery(true) ->update('#__osmap_items_settings') ->set('published = 0') ->where([ 'sitemap_id = ' . $db->quote($sitemap->id), 'uid = ' . $db->quote($uid) ]); } $db->setQuery($query)->execute(); } } } // Convert custom settings for items $query = $db->getQuery(true) ->select([ 'uid', 'properties' ]) ->from('#__xmap_items') ->where('sitemap_id = ' . $db->quote($sitemap->id)) ->where('view = ' . $db->quote('xml')); if ($modifiedItems = $db->setQuery($query)->loadObjectList()) { foreach ($modifiedItems as $item) { $item->properties = str_replace(';', '&', $item->properties); parse_str($item->properties, $properties); $item->uid = $this->convertItemUID($item->uid); // Check if the item already exists to update, or insert $query = $db->getQuery(true) ->select('COUNT(*)') ->from('#__osmap_items_settings') ->where([ 'sitemap_id = ' . $db->quote($sitemap->id), 'uid = ' . $db->quote($item->uid) ]); $exists = (bool)$db->setQuery($query)->loadResult(); if ($exists) { $fields = []; // Check if the changefreq is set and set to update if (isset($properties['changefreq'])) { $fields = 'changefreq = ' . $db->quote($properties['changefreq']); } // Check if the priority is set and set to update if (isset($properties['priority'])) { $fields = 'priority = ' . $db->quote($properties['priority']); } // Update the item $query = $db->getQuery(true) ->update('#__osmap_items_settings') ->set($fields) ->where([ 'sitemap_id = ' . $db->quote($sitemap->id), 'uid = ' . $db->quote($item->uid) ]); } else { $columns = [ 'sitemap_id', 'uid', 'published' ]; $values = [ $db->quote($sitemap->id), $db->quote($item->uid), 1 ]; // Check if the changefreq is set and set to update if (isset($properties['changefreq'])) { $columns[] = 'changefreq'; $values[] = 'changefreq = ' . $db->quote($properties['changefreq']); } // Check if the priority is set and set to update if (isset($properties['priority'])) { $columns[] = 'priority'; $values[] = 'priority = ' . $db->quote($properties['priority']); } // Insert a new item $query = $db->getQuery(true) ->insert('#__osmap_items_settings') ->columns($columns) ->values(implode(',', $values)); } $db->setQuery($query)->execute(); } } } else { $sitemapFailedIds = $sitemap->id; } } } if ($sitemapFailedIds || $itemFailedIds) { throw new \Exception('Failed the sitemap or item migration'); } /* * Menu Migration */ $xmap = new Generic('Xmap', 'component'); $osmap = new Generic('OSMap', 'component'); // Remove OSMap menus $query = $db->getQuery(true) ->delete('#__menu') ->where('type = ' . $db->quote('component')) ->where('component_id = ' . $db->quote($osmap->getId())); $db->setQuery($query); $db->execute(); // Get the Xmap menus $query = $db->getQuery(true) ->select('*') ->from('#__menu') ->where('type = ' . $db->quote('component')) ->where('component_id = ' . $db->quote($xmap->getId())); $db->setQuery($query); $xmapMenus = $db->loadObjectList(); if (!empty($xmapMenus)) { // Convert each menu to OSMap foreach ($xmapMenus as $menu) { $query = $db->getQuery(true) ->set('title = ' . $db->quote($this->replaceXmapByOSMap($menu->title))) ->set('alias = ' . $db->quote($this->replaceXmapByOSMap($menu->alias))) ->set('path = ' . $db->quote($this->replaceXmapByOSMap($menu->path))) ->set('link = ' . $db->quote($this->replaceXmapByOSMap($menu->link))) ->set('img = ' . $db->quote($this->replaceXmapByOSMap($menu->img))) ->set('component_id = ' . $db->quote($osmap->getId())) ->update('#__menu') ->where('id = ' . $db->quote($menu->id)); $db->setQuery($query); $db->execute(); } } // Disable Xmap $query = $db->getQuery(true) ->set('enabled = 0') ->update('#__extensions') ->where('extension_id = ' . $db->quote($xmap->getId())); $db->setQuery($query); $db->execute(); // Clean up Xmap db tables $db->setQuery('DELETE FROM ' . $db->quoteName('#__xmap_sitemap')); $db->execute(); $db->setQuery('DELETE FROM ' . $db->quoteName('#__xmap_items')); $db->execute(); $db->transactionCommit(); $result->success = true; } catch (\Exception $e) { $db->transactionRollback(); var_dump($e); } echo json_encode($result); } /** * Replaces the Xmap strings in multiple formats, changing to OSMap. * * @param string $str * * @return string */ protected function replaceXmapByOSMap(string $str): string { $replacements = [ 'XMAP' => 'OSMAP', 'XMap' => 'OSMap', 'xMap' => 'OSMap', 'xmap' => 'osmap', ]; return str_replace( array_keys($replacements), $replacements, $str ); } /** * Returns the id of the menutype. * * @param string $menuType * * @return int */ protected function getMenuTypeId(string $menuType): int { $db = Factory::getDbo(); $query = $db->getQuery(true) ->select('id') ->from('#__menu_types') ->where('menutype = ' . $db->quote($menuType)); return (int)$db->setQuery($query)->loadResult(); } /** * Converts a legacy UID to the new pattern. Instead of "com_contenta25", * "joomla.article.25". Returns the new UID * * @param string $uid * * @return string */ protected function convertItemUID(string $uid): string { // Joomla articles in categories if (preg_match('#com_contentc[0-9]+a([0-9]+)#', $uid, $matches)) { return 'joomla.article.' . $matches[1]; } // Joomla categories if (preg_match('#com_contentc([0-9]+)#', $uid, $matches)) { return 'joomla.category.' . $matches[1]; } // Joomla articles if (preg_match('#com_contenta([0-9]+)#', $uid, $matches)) { return 'joomla.article.' . $matches[1]; } // Joomla featured if (preg_match('#com_contentfeatureda([0-9]+)#', $uid, $matches)) { return 'joomla.featured.' . $matches[1]; } // Menu items if (preg_match('#itemid([0-9]*)#', $uid, $matches)) { return 'menuitem.' . $matches[1]; } return $uid; } }