Sindbad~EG File Manager

Current Path : /home/sarayaglobal/sarayamystair.in/administrator/components/com_sh404sef/
Upload File :
Current File : /home/sarayaglobal/sarayamystair.in/administrator/components/com_sh404sef/installation.script.php

<?php
/**
 * sh404SEF - SEO extension for Joomla!
 *
 * @package      sh404SEF
 * @copyright    (c) Yannick Gaultier - Weeblr llc - 2020
 * @author       Yannick Gaultier
 * @license      http://www.gnu.org/copyleft/gpl.html GNU/GPL
 * @version      4.21.1.4217
 * @date        2020-09-23
 */

// Security check to ensure this file is being included by a parent file.
defined('_JEXEC') or die;

/**
 * Installation/Uninstallation script
 *
 */
class Com_Sh404sefInstallerScript
{
	const MIN_JOOMLA_VERSION = '3.9.0';
	const MAX_JOOMLA_VERSION = '4.0';

	const MIN_PHP_VERSION = '5.6.0';
	const MAX_PHP_VERSION = '';

	const MIN_SHLIB_VERSION = '0.4.0';
	const MAX_SHLIB_VERSION = '';

	private $_siteId               = '';
	private $_preserveConfigFolder = '';
	private $_errorPageCatId       = 0;
	private $_shlibVersion         = '';
	private $_skipInstall          = array();

	private $hasUtf8mb4Support = false;
	private $charset           = 'utf8';
	private $collation         = 'utf8_general_ci';

	public function preflight($route, $installer)
	{
		if ($route == 'install' || $route == 'update')
		{
			// check Joomla! version
			if (version_compare(JVERSION, self::MIN_JOOMLA_VERSION, '<') || version_compare(JVERSION, self::MAX_JOOMLA_VERSION, 'ge'))
			{
				JFactory::getApplication()
				        ->enqueueMessage(
					        sprintf(
						        'sh404SEF requires Joomla! version between %s and %s (you are using %s). Aborting installation',
						        self::MIN_JOOMLA_VERSION, self::MAX_JOOMLA_VERSION, JVERSION
					        ), 'error'
				        );

				return false;
			}

			if (
				version_compare(phpversion(), self::MIN_PHP_VERSION, '<')
				||
				(
					!empty(self::MAX_PHP_VERSION)
					&&
					version_compare(
						phpversion(), self::MAX_PHP_VERSION, 'ge'
					)
				)
			)
			{
				JFactory::getApplication()
				        ->enqueueMessage(
					        sprintf(
						        'sh404SEF requires PHP version between %s and %s (you are using %s). Aborting installation',
						        self::MIN_PHP_VERSION, self::MAX_PHP_VERSION, phpversion()
					        ), 'error'
				        );

				return false;
			}

			// make sure resource manager is available, we'll need it during plugins installs
			if (!class_exists('ShlSystem_Resourcemanager'))
			{
				require_once $installer->getParent()->getPath('source') . '/admin/plugins/system/shlib/shl_packages/system/resourcemanager.php';
			}

			// check authorization to install for shared resources
			$newVersionFile      = $installer->getParent()->getPath('source') . '/admin/plugins/system/shlib/shlib.xml';
			$this->_shlibVersion = ShlSystem_Resourcemanager::getXmlFileVersion($newVersionFile);
			$installCheckResult  = ShlSystem_Resourcemanager::canInstall('shlib', $this->_shlibVersion, $allowDowngrade = false, self::MIN_SHLIB_VERSION, self::MAX_SHLIB_VERSION);

			if ($installCheckResult->canInstall == 'no')
			{
				JFactory::getApplication()
				        ->enqueueMessage(
					        'Cannot install sh404SEF: not allowed to install shLib version ' . $this->_shlibVersion . ': ' . $installCheckResult->reason,
					        'error'
				        );
			}
			if ($installCheckResult->canInstall == 'skip')
			{
				$this->_shlibVersion  = '';
				$this->_skipInstall[] = 'shlib';
				JFactory::getApplication()
				        ->enqueueMessage('shLib: skipping install of shLib version ' . $this->_shlibVersion . ': ' . $installCheckResult->reason);
			}

			$canInstall = $installCheckResult->canInstall != 'no';

			return $canInstall;
		}
	}

	/**
	 * Insert in the db the previously retrieved parameters for a plugin
	 * including publication information. Also move files as required
	 *
	 * @param string $basePath , the base path to get original files from
	 */

	public function postflight($type, $parent)
	{
		if (function_exists('apc_clear_cache'))
		{
			apc_clear_cache();
		}
		if (function_exists('opcache_reset'))
		{
			opcache_reset();
		}

		$this->_doInstallUpdate($parent);

		// installed a shared resource? register it with version
		if (!in_array('shlib', $this->_skipInstall))
		{
			ShlSystem_Resourcemanager::registerResource('shlib', $this->_shlibVersion);
		}

		// register that we are now using shLib
		ShlSystem_Resourcemanager::register(array('resource' => 'shlib', 'context' => 'com_sh404sef', 'min_version' => self::MIN_SHLIB_VERSION, 'max_version' => self::MAX_SHLIB_VERSION));

		// J3.6.1+ need language filter to be before sh404SEF system plg, change in initialization code
		$this->fixPluginsOrder();

		// make sure the update site is appropriate
		$this->_processUpdateSite($type, $parent);

		// clear caches, in case cache handling or remote config has been modified
		$caches = array('sh404sef_updates', 'sh404sef_rconfig', 'sh404sef_analytics_auth', 'sh404sef_analytics');
		foreach ($caches as $cacheName)
		{
			$cache = JFactory::getCache($cacheName);
			$cache->clean();
		}
	}

	public function install($parent)
	{
	}

	public function uninstall($parent)
	{
		// save configuration
		try
		{
			$this->_definePaths();

			$db    = JFactory::getDbo();
			$query = $db->getQuery(true);
			$query->select('params')
			      ->from('#__extensions')
			      ->where($db->qn('type') . '=' . $db->q('component'))
			      ->where($db->qn('element') . '=' . $db->q('com_sh404sef'));
			$db->setQuery($query);
			$sh404sef = $db->loadObject();

			$saved = $this->_writeExtensionConfig('com_sh404sef', array('sh404sefConfig' => $sh404sef->params));
			if (!$saved)
			{
				JFactory::getApplication()->enqueueMessage('Error saving sh404SEF configuration to disk, configuration may be lost upon reinstallation');
			}
		}
		catch (\Exception $e)
		{
			JFactory::getApplication()->enqueueMessage('Error saving sh404SEF configuration to disk, configuration may be lost upon reinstallation');
		}

		$this->_doUninstall($parent);
	}

	public function update($parent)
	{
	}

	private function _definePaths()
	{
		$this->_siteId               = rtrim(str_replace('/administrator', '', JURI::base()), '/');
		$this->_siteId               = str_replace('/', '_', str_replace('http://', '', $this->_siteId));
		$this->_preserveConfigFolder = JPATH_ROOT . '/media/sh404sef/';
	}

	// Implementation of install/uninstall scripts

	/**
	 * Writes an extension parameter to a disk file, located
	 * in the /media directory
	 *
	 * @param string $extName the extension name
	 * @param        $params
	 *
	 * @return boolean, true if no error
	 */
	private function _writeExtensionConfig($extName, $params)
	{
		if (empty($params))
		{
			return;
		}

		// calculate target file name
		$extPath = $this->_preserveConfigFolder . 'sh404_upgrade_conf';

		// if it does not exists, lets create it first
		if (!JFolder::exists($extPath))
		{
			JFolder::create($extPath);
		}

		// make sure we have an index.html file in that folder
		$target = JPath::clean($extPath . '/' . 'index.html');
		if (!JFile::exists($target))
		{
			// copy one Joomla's index.html file to the backup directory
			$source  = JPath::clean(JPATH_ROOT . '/plugins/index.html');
			$success = JFile::copy($source, $target);
		}

		// now build full path file name to save config
		$extFile = $extPath . '/' . $extName . '_' . $this->_siteId . '.php';

		// remove previous if any
		if (JFile::exists($extFile))
		{
			JFile::delete($extFile);
		}

		// prepare data for writing
		$data = '<?php // Extension params save file for sh404sef
    //
    if (!defined(\'_JEXEC\')) die(\'Direct Access to this location is not allowed.\');';
		$data .= "\n";

		if (!empty($params))
		{
			foreach ($params as $key => $value)
			{
				$data .= '$' . $key . ' = ' . var_export($value, true) . ';';
				$data .= "\n";
			}
		}

		// write to disk
		$success = JFile::write($extFile, $data);

		return $success !== false;
	}

	/**
	 * Performs pre-uninstall backup of configuration
	 *
	 * @param object $parent
	 */
	private function _doUninstall($parent)
	{
		$this->_definePaths();
		$this->_includeLibs();

		// Before uninstalling modules, save their settings, if told to do so
		$sef_config_class = JPATH_ADMINISTRATOR . '/components/com_sh404sef/sh404sef.class.php';

		// Make sure class was loaded.
		if (!class_exists('shSEFConfig'))
		{
			if (is_readable($sef_config_class))
			{
				require_once($sef_config_class);
			}
			else
			{
				JError::RaiseError(500, JText::_('COM_SH404SEF_NOREAD') . "( $sef_config_class )<br />" . JText::_('COM_SH404SEF_CHK_PERMS'));
			}
		}
		$sefConfig = new shSEFConfig();
		if (!$sefConfig->shKeepStandardURLOnUpgrade && !$sefConfig->shKeepCustomURLOnUpgrade)
		{
			$this->_deletetable('sh404sef_urls');
			$this->_deletetable('sh404sef_urls_src');
			$this->_deletetable('sh404sef_hits_404s');
			$this->_deletetable('sh404sef_aliases');
			$this->_deletetable('sh404sef_hits_aliases');
			$this->_deletetable('sh404sef_pageids');
			$this->_deletetable('sh404sef_hits_shurls');
		}
		elseif (!$sefConfig->shKeepStandardURLOnUpgrade)
		{
			$this->_deleteAllSEFUrl('Standard');
			$this->_deletetable('sh404sef_urls_src');
			$this->_deletetable('sh404sef_hits_404s');
		}
		elseif (!$sefConfig->shKeepCustomURLOnUpgrade)
		{
			$this->_deleteAllSEFUrl('Custom');
			$this->_deletetable('sh404sef_aliases');
			$this->_deletetable('sh404sef_hits_aliases');
			$this->_deletetable('sh404sef_pageids');
			$this->_deletetable('sh404sef_hits_shurls');
		}

		if (!$sefConfig->shKeepMetaDataOnUpgrade)
		{
			$this->_deletetable('sh404sef_metas');
		}

		// delete key store, content (error pages) has been saved before
		$this->_deletetable('sh404sef_keystore');

		// remove system plugins
		$this->_saveDeletePluginParams('sh404sef', 'system');
		$this->_saveDeletePluginParams('shmobile', 'system');

		// remove installer plugin
		$this->_saveDeletePluginParams('sh404sef', 'installer');

		// unregister from shLib, then possibly uninstall it
		if (JFile::exists(JPATH_ROOT . '/plugins/system/shlib/shl_packages/system/resourcemanager.php'))
		{
			require_once JPATH_ROOT . '/plugins/system/shlib/shl_packages/system/resourcemanager.php';
			ShlSystem_Resourcemanager::unregister('shlib', 'com_sh404sef');
			if (ShlSystem_Resourcemanager::canUninstall('shlib'))
			{
				$this->_saveDeletePluginParams('shlib', 'system');
			}
		}

		// remove core plugins
		$this->_saveDeletePluginGroup('sh404sefcore');
		$this->_saveDeletePluginGroup('sh404sefextplugins');

		// delete analytics cached data, to force update
		// in case this part of sh404sef has changed
		$cache = JFactory::getCache('sh404sef_analytics');
		$cache->clean();

		// preserve configuration or not ?
		if (!$sefConfig->shKeepConfigOnUpgrade)
		{
			// main config file
			$fileName = $this->_preserveConfigFolder . 'sh404_upgrade_conf_' . $this->_siteId . '.php';
			if (JFile::exists($fileName))
			{
				JFile::delete($fileName);
			}

			// user custom config file
			$fileName = $this->_preserveConfigFolder . 'sh404_upgrade_conf_' . $this->_siteId . '.custom.php';
			if (JFile::exists($fileName))
			{
				JFile::delete($fileName);
			}

			// related extensions (plugins) config files folder
			if (JFolder::exists($this->_preserveConfigFolder . 'sh404_upgrade_conf'))
			{
				JFolder::delete($this->_preserveConfigFolder . 'sh404_upgrade_conf');
			}

			// log files folder
			if (JFolder::exists($this->_preserveConfigFolder . 'sh404_upgrade_conf_logs'))
			{
				JFolder::delete($this->_preserveConfigFolder . 'sh404_upgrade_conf_logs');
			}

			// security log files folder
			if (JFolder::exists($this->_preserveConfigFolder . 'sh404_upgrade_conf_security'))
			{
				JFolder::delete($this->_preserveConfigFolder . 'sh404_upgrade_conf_security');
			}
		}
		else
		{ // if we keep config

			if (JFolder::exists(JPATH_ROOT . '/logs/sh404sef'))
			{
				JFolder::copy(JPATH_ROOT . '/logs/sh404sef', $this->_preserveConfigFolder . 'sh404_upgrade_conf_logs', $path = '', $force = true);
			}

			if (JFolder::exists(JPATH_ADMINISTRATOR . '/components/com_sh404sef/security'))
			{
				JFolder::copy(
					JPATH_ADMINISTRATOR . '/components/com_sh404sef/security',
					$this->_preserveConfigFolder . 'sh404_upgrade_conf_security', $path = '', $force = true
				);
			}
		}

		// delete folder in /media
		// display results
		echo '<h3>sh404SEF has been succesfully uninstalled. </h3>';
		echo '<br />';
		if ($sefConfig->shKeepStandardURLOnUpgrade)
		{
			echo '- automatically generated SEF url have not been deleted (table #__sh404sef_urls)<br />';
		}
		else
		{
			echo '- automatically generated SEF url have been deleted<br />';
		}
		echo '<br />';
		if ($sefConfig->shKeepCustomURLOnUpgrade)
		{
			echo '- custom SEF url, aliases and pageIds have not been deleted (tables #__sh404sef_urls, #__sh404sef_aliases and #__sh404sef_pageids)<br />';
		}
		else
		{
			echo '- custom SEF url, aliases and pageIds have been deleted<br />';
		}
		echo '<br />';
		if ($sefConfig->shKeepMetaDataOnUpgrade)
		{
			echo '- Custom Title and META data have not been deleted (table #__sh404sef_metas)<br />';
		}
		else
		{
			echo '- Custom Title and META data have been deleted<br />';
		}
		echo '<br />';
	}

	private function _includeLibs()
	{
		jimport('joomla.filesystem.file');
		jimport('joomla.filesystem.folder');
		jimport('joomla.filesystem.path');
		jimport('joomla.html.parameter');
		jimport('joomla.filter.filterinput');
		jimport('joomla.utilities.string');

		// we use require instead of require_once so that the NEW, just installed, version of the file is
		// reloaded. If any new define has been added in the new version, they will thus become available
		// This requires that defines in defines.php are protected again redefining the same constant
		require JPATH_ROOT . '/administrator/components/com_sh404sef/defines.php';
		// if these files have been included already, we will use the old version of the file
		require_once JPATH_ROOT . '/administrator/components/com_sh404sef/exceptions/default.php';
		require_once JPATH_ROOT . '/administrator/components/com_sh404sef/helpers/language.php';
	}

	private function _deletetable($tableName)
	{
		try
		{
			$db    = JFactory::getDbo();
			$query = 'DROP TABLE IF EXISTS ' . $db->qn('#__' . $tableName);
			$db->setQuery($query)->query();
		}
		catch (\Exception $e)
		{
			JFactory::getApplication()->enqueuemessage($e->getMessage(), 'error');
		}
	}

	private function _deleteAllSEFUrl($kind)
	{
		try
		{
			$db = JFactory::getDbo();
			if ($kind == 'Custom')
			{
				$where = $db->qn('dateadd') . ' > ' . $db->q('0000-00-00') . ' and ' . $db->qn('newurl') . ' != ' . $db->q('');
			}
			else
			{
				$where = $db->qn('dateadd') . ' = ' . $db->q('0000-00-00');
			}

			$db->setQuery('DELETE FROM ' . $db->qn('#__sh404sef_urls') . ' ' . $where);
			$db->query();
		}
		catch (\Exception $e)
		{
			JFactory::getApplication()->enqueuemessage($e->getMessage(), 'error');
		}
	}

	/**
	 * Save parameters, then delete a module
	 * Would not work on additional copies made by user
	 *
	 * @param string $moduleName , the module name, matching 'module' column in modules table
	 * @param string $client (ie : site or administrator
	 *
	 * @return bool
	 * @throws Exception
	 */
	private function _saveDeleteModuleParams($moduleName, $client)
	{
		// read plugin param from db
		try
		{
			$db = JFactory::getDbo();

			//$result = ShlDbHelper::selectAssoc('#__extensions', array('*'),
			//	array('type' => 'module', 'element' => $moduleName, 'client_id' => $client));
			$query = $db->getQuery(true);
			$query->select('*')
			      ->from('#__extensions')
			      ->where($db->qn('type') . '=' . $db->q('module'))
			      ->where($db->qn('element') . '=' . $db->q($moduleName))
			      ->where($db->qn('client_id') . '=' . $db->q($client));
			$db->setQuery($query);
			$result = $db->loadAssoc();

			if (empty($result))
			{
				// invalid module name?
				return false;
			}
			// remove module db id
			unset($result['extension_id']);

			// write everything on disk
			$this->_writeExtensionConfig($moduleName . '_extension', array('shConfig' => $result));

			// now remove plugin details from db
			//ShlDbHelper::delete('#__extensions', array('type' => 'module', 'element' => $moduleName, 'client_id' => $client));
			$query = $db->getQuery(true);
			$query->delete('#__extensions')
			      ->where($db->qn('type') . '=' . $db->q('module'))
			      ->where($db->qn('element') . '=' . $db->q($moduleName))
			      ->where($db->qn('client_id') . '=' . $db->q($client));
			$db->setQuery($query)->query();

			// do the same for the module instance, in #__module table
			//$result = ShlDbHelper::selectAssoc('#__modules', array('*'), array('module' => $moduleName, 'client_id' => $client));
			$query = $db->getQuery(true);
			$query->select('*')
			      ->from('#__modules')
			      ->where($db->qn('module') . '=' . $db->q($moduleName))
			      ->where($db->qn('client_id') . '=' . $db->q($client));
			$db->setQuery($query);
			$result = $db->loadAssoc();

			if (empty($result))
			{
				// invalid module name?
				return false;
			}
			// save and remove module db id
			$moduleId = $result['id'];
			unset($result['id']);

			// write everything on disk
			$this->_writeExtensionConfig($moduleName . '_modules', array('shConfig' => $result));

			// now remove plugin details from db
			//ShlDbHelper::delete('#__modules', array('module' => $moduleName, 'client_id' => $client));
			$query = $db->getQuery(true);
			$query->delete('#__modules')
			      ->where($db->qn('module') . '=' . $db->q($moduleName))
			      ->where($db->qn('client_id') . '=' . $db->q($client));
			$db->setQuery($query)->query();

			// remove module/menu affectation
			//$result = ShlDbHelper::selectAssoc('#__modules_menu', array('*'), array('moduleid' => $moduleId));
			$query = $db->getQuery(true);
			$query->select('*')
			      ->from('#__modules_menu')
			      ->where($db->qn('moduleid') . '=' . $db->q($moduleId));
			$db->setQuery($query);
			$result = $db->loadAssoc();

			// remove module db id
			unset($result['moduleid']);

			// write everything on disk
			$this->_writeExtensionConfig($moduleName . '_modules_menu', array('shConfig' => $result));

			// now remove plugin details from db
			//ShlDbHelper::delete('#__modules_menu', array('moduleid' => $moduleId));
			$query = $db->getQuery(true);
			$query->delete('#__modules_menu')
			      ->where($db->qn('moduleid') . '=' . $db->q($moduleId));
			$db->setQuery($query)->query();
		}
		catch (\Exception $e)
		{
			JFactory::getApplication()->enqueuemessage($e->getMessage(), 'error');
		}

		// delete the module files
		$path = JPATH_ROOT . '/' . ($client ? 'administrator' . '/' : '') . 'modules/' . $moduleName;
		if (JFolder::exists($path))
		{
			JFolder::delete($path);
		}
	}

	/**
	 * Save parameters, then delete a plugin
	 *
	 * @param string $pluginName , the plugin name, mathcing 'element' column in plugins table
	 * @param string $folder , the plugin folder (ie : 'content', 'search', 'system',...
	 * @param null   $folders
	 *
	 * @return bool
	 * @throws Exception
	 */
	private function _saveDeletePluginParams($pluginName, $folder, $folders = null)
	{
		try
		{
			$db = JFactory::getDbo();

			//$result = ShlDbHelper::selectAssoc('#__extensions', array('*'), array('type' => 'plugin', 'element' => $pluginName, 'folder' => $folder));
			$query = $db->getQuery(true);
			$query->select('*')
			      ->from('#__extensions')
			      ->where($db->qn('type') . '=' . $db->q('plugin'))
			      ->where($db->qn('element') . '=' . $db->q($pluginName))
			      ->where($db->qn('folder') . '=' . $db->q($folder));
			$db->setQuery($query);
			$result = $db->loadAssoc();

			if (empty($result))
			{
				// invalid plugin name?
				return false;
			}

			// remove plugin db id
			$pluginId = $result['extension_id'];
			unset($result['extension_id']);

			// write everything on disk
			$this->_writeExtensionConfig($pluginName, array('shConfig' => $result));

			// now uninstall
			$installer = new JInstaller;
			$result    = $installer->uninstall('plugin', $pluginId);
		}
		catch (\Exception $e)
		{
			JFactory::getApplication()->enqueuemessage($e->getMessage(), 'error');
		}
	}

	/**
	 * Save params, then delete plugin, for all plugins
	 * in a given group
	 *
	 * @param $group the group to be deleted
	 *
	 * @return bool
	 * @throws Exception
	 */
	private function _saveDeletePluginGroup($group)
	{
		$unsafe = array('authentication', 'content', 'editors', 'editors-xtd', 'search', 'system', 'xmlrpc');
		if (in_array($group, $unsafe))
		{
			// safety net : we don't want to delete the whole system or content folder
			return false;
		}

		// read plugin param from db
		try
		{
			$db = JFactory::getDbo();
			// $pluginList = ShlDbHelper::selectAssocList('#__extensions', array('*'), array('type' => 'plugin', 'folder' => $group));

			$query = $db->getQuery(true);
			$query->select('*')
			      ->from('#__extensions')
			      ->where($db->qn('type') . '=' . $db->q('plugin'))
			      ->where($db->qn('folder') . '=' . $db->q($group));
			$db->setQuery($query);
			$pluginList = $db->loadAssocList();

			if (empty($pluginList))
			{
				return true;
			}

			// for each plugin
			foreach ($pluginList as $plugin)
			{
				// remove plugin db id
				unset($plugin['id']);

				// write everything on disk
				$this->_writeExtensionConfig($plugin['folder'] . '.' . $plugin['element'], array('shConfig' => $plugin));

				// now remove plugin details from db
				//ShlDbHelper::delete('#__extensions', array('type' => 'plugin', 'element' => $plugin['element'], 'folder' => $plugin['folder']));
				$query = $db->getQuery(true);
				$query->delete('#__extensions')
				      ->where($db->qn('type') . '=' . $db->q('plugin'))
				      ->where($db->qn('element') . '=' . $db->q($plugin['element']))
				      ->where($db->qn('folder') . '=' . $db->q($plugin['folder']));
				$db->setQuery($query)->query();
			}
		}
		catch (\Exception $e)
		{
			JFactory::getApplication()->enqueuemessage($e->getMessage(), 'error');
		}

		// now delete the files for the whole group
		if (JFolder::exists(JPATH_ROOT . '/plugins/' . $group))
		{
			JFolder::delete(JPATH_ROOT . '/plugins/' . $group);
		}
	}

	private function _doInstallUpdate($parent)
	{
		$this->_definePaths();
		$this->_includeLibs();
		$db = JFactory::getDbo();

		// include default functions file
		$path = JPATH_ROOT . '/libraries/weeblr';
		if (!JFolder::exists($path))
		{
			JFolder::create($path);
		}
		if (!JFile::exists($path . '/sh404sef_functions.php'))
		{
			JFile::copy(
				JPATH_ADMINISTRATOR . '/components/com_sh404sef/sh404sef_functions.php',
				$path . '/sh404sef_functions.php'
			);
		}

		// Copy existing config file from /media to current component. Used to recover configuration when upgrading
		// check if old file exists before deleting stub config file
		$oldConfigFile = $this->_preserveConfigFolder . 'sh404_upgrade_conf_' . $this->_siteId . '.php';
		if (JFile::exists($oldConfigFile))
		{
			// update old config files from VALID_MOS check to _JEXEC
			$config = JFile::read($oldConfigFile);
			if ($config && strpos($config, 'VALID_MOS') !== false)
			{
				$config = str_replace('VALID_MOS', '_JEXEC', $config);
				JFile::write($oldConfigFile, $config); // write it back
			}
			// now get back old config
			if (JFile::exists(JPATH_ADMINISTRATOR . '/components/com_sh404sef/config/config.sef.php'))
			{
				JFile::delete(JPATH_ADMINISTRATOR . '/components/com_sh404sef/config/config.sef.php');
			}
			JFile::copy($oldConfigFile, JPATH_ADMINISTRATOR . '/components/com_sh404sef/config/config.sef.php');
		}

		// restore black/white lists
		$folder = $this->_preserveConfigFolder . 'sh404_upgrade_conf_security';
		if (JFolder::exists($folder))
		{
			$fileList = JFolder::files($folder);
			if (!empty($fileList))
			{
				foreach ($fileList as $file)
				{
					if (JFile::exists(JPATH_ADMINISTRATOR . '/components/com_sh404sef/security/' . $file))
					{
						JFile::delete(JPATH_ADMINISTRATOR . '/components/com_sh404sef/security/' . $file);
					}
					JFile::copy(
						$this->_preserveConfigFolder . 'sh404_upgrade_conf_security/' . $file,
						JPATH_ADMINISTRATOR . '/components/com_sh404sef/security/' . $file
					);
				}
			}
		}
		// if upgrading rather than installing from scratch, or after an uninstall
		// we must not copy back saved configuration files and log files
		// as this would overwrite up to date current ones
		// note that above we restored main config file and
		// security data files becomes blank files come
		// with the extension, so they'll be deleted in any case
		// and we have to restore them
		$shouldRestore = $this->_shouldRestore();

		if ($shouldRestore)
		{
			// restore log files
			$folder = $this->_preserveConfigFolder . 'sh404_upgrade_conf_logs';
			if (JFolder::exists($folder))
			{
				JFolder::copy($folder, JPATH_ROOT . '/logs/sh404sef', $path = '', $force = true);
			}

			// restore customized default params
			$oldCustomConfigFile = $this->_preserveConfigFolder . 'sh404_upgrade_conf_' . $this->_siteId . '.custom.php';
			if (is_readable($oldCustomConfigFile) && filesize($oldCustomConfigFile) > 1000)
			{
				// update old config files from VALID_MOS check to _JEXEC
				$config = JFile::read($oldCustomConfigFile);
				if ($config && strpos($config, 'VALID_MOS') !== false)
				{
					$config = str_replace('VALID_MOS', '_JEXEC', $config);
					JFile::write($oldCustomConfigFile, $config); // write it back
				}
				if (JFile::exists(JPATH_ADMINISTRATOR . '/components/com_sh404sef/custom.sef.php'))
				{
					JFile::delete(JPATH_ADMINISTRATOR . '/components/com_sh404sef/custom.sef.php');
				}
				$result = JFile::copy($oldCustomConfigFile, JPATH_ADMINISTRATOR . '/components/com_sh404sef/custom.sef.php');
			}

			// read saved config
			$extFile = $this->_preserveConfigFolder . 'sh404_upgrade_conf' . '/com_sh404sef_' . $this->_siteId . '.php';

			// remove previous if any
			if (JFile::exists($extFile))
			{
				include_once $extFile;
				if (!empty($sh404sefConfig))
				{
					// write it back into sh404sef config field
					try
					{
						$query = 'update ' . $db->qn('#__extensions') . ' set ' . $db->qn('params') . ' = ' . $db->q($sh404sefConfig)
							. ' where ' . $db->qn('type') . ' = ' . $db->q('component') . ' and '
							. $db->qn('element') . ' = ' . $db->q('com_sh404sef');
						$db->setQuery($query)->query();
					}
					catch (\Exception $e)
					{
						JFactory::getApplication()
						        ->enqueuemessage('Database error while restoring saved configuration. Config may be lost: ' . $e->getMessage());
					}
				}
			}
		}

		// install plugins
		if (defined('SHLIB_ROOT_PATH'))
		{
			// trick of the day: we must fetch an instance of the db using the db helper
			// before installing the newest version of shLib system plugin. This will
			// force a decorated db instance to be created and stored, using the shlib
			// db class version that matches that of the shlib db helper class
			// As there was interface changes betwen shLib 0.1.x and 0.2.x, this prevents
			// "method not existing" errors when installing a newer version over an old one
			// make sure resource manager is available, we'll need it during plugins installs
			$shlDb  = ShlDbHelper::getInstance();
			$status = $this->_installPluginGroup('system');
		}
		else
		{
			$this->_installPluginGroup('system');
			// shLib is not installed yet, let's make it available to us
			if (!JFile::exists(JPATH_ROOT . '/plugins/system/shlib/shlib.php'))
			{
				JFactory::getApplication()
				        ->enqueuemessage('shLib was not installed properly, cannot continue. Please try uninstalling and installing again');

				return false;
			}
			require_once JPATH_ROOT . '/plugins/system/shlib/shlib.php';
			$config = array('type' => 'system', 'name' => 'shlib', 'params' => '');
			if (version_compare(JVERSION, '3', 'ge'))
			{
				$dispatcher = JEventDispatcher::getInstance();
			}
			else
			{
				$dispatcher = JDispatcher::getInstance();
			}
			$shLibPlugin = new plgSystemShlib($dispatcher, $config);
			$shLibPlugin->onAfterInitialise();
			$status = true;
		}

		if (!$status)
		{
			JFactory::getApplication()
			        ->enqueuemessage('There was an error installing one or more system plugins. Please try uninstalling and installing again');

			return;
		}

		$status = $this->_installPluginGroup('sh404sefcore');
		if (!$status)
		{
			JFactory::getApplication()
			        ->enqueuemessage('There was an error installing one or more sh404SEF core plugins. Please try uninstalling and installing again');

			return;
		}
		$status = $this->_installPluginGroup('installer');
		if (!$status)
		{
			JFactory::getApplication()
			        ->enqueuemessage('There was an error installing one or more sh404SEF installer plugins. Please try uninstalling and installing again');

			return;
		}
		$status = $this->_installPluginGroup('sh404sefextplugins');
		if (!$status)
		{
			JFactory::getApplication()
			        ->enqueuemessage('There was an error installing one or more sh404SEF extension plugins. Please try uninstalling and installing again');

			return;
		}

		// remove cpicon module, not needed anymore that we use Joomla updater
		// remove admin quick icon module
		$this->_saveDeleteModuleParams('mod_sh404sef_cpicon', $client = 1);

		// apply various DB updates
		$this->_createDBStructure();
		$this->_updateDBStructure();

		// now we insert the 404 error page into the database
		// from version 1.5.5, the default content of 404 page has been largely modified
		// to make use of the similar urls plugin (and potentially others)
		// so we want to make sure people will have the new version of the 404 error page
		$status = $this->_updateErrorPage();
		if (!$status)
		{
			JFactory::getApplication()
			        ->enqueuemessage('There was an error updating the 404 error page. You may have to set it up manually in sh404SEF configuration.');
		}

		$status = $this->_updateAnalyticsAccount();
		if (!$status)
		{
			JFactory::getApplication()
			        ->enqueuemessage('There was an error updating the Google Analytics Web property ID. You may have to set it up manually in sh404SEF configuration.');
		}

		$status = $this->_fixInvalidShurl();
		if (!$status)
		{
			JFactory::getApplication()
			        ->enqueuemessage('There was an error fixing some invalid shURLs in the database. Please check that no shURL is identical to one of your language codes ie /en, /fr, /de.');
		}

		$status = $this->_transferCanonical();
		if (!$status)
		{
			JFactory::getApplication()
			        ->enqueuemessage('There was an error tranfering canonical links from the meta data table to the aliases table (new in version 4.12.0). Please take note of the full error message, double-check your canonicals, or try installing again.');
		}

		$status = $this->_reorderAliases();
		if (!$status)
		{
			JFactory::getApplication()
			        ->enqueuemessage('There was an error when re-ordering aliases in the database. This should not prevent normal operation, but please take note of the full error message and try installing again.');
		}

		// make sur permissions record are set straight
		$query = $db->getQuery(true);
		$query->select('rules');
		$query->from('#__assets');
		$query->where($db->qn('name') . '=' . $db->q('com_sh404sef'));
		$db->setQuery($query);
		$existingRules = $db->loadResult();
		if (empty($existingRules) || $existingRules == '{}' || $existingRules == '[]')
		{
			// write valid default value into assets record
			$defaultRule = '{"core.admin":[],"core.manage":[],"core.create":[],"core.delete":[],"core.edit":[],"core.edit.state":[],"core.edit.own":[]}';
			$query->update($db->qn('#__assets'))->set($db->qn('rules') . '=' . $db->q($defaultRule))
			      ->where($db->qn('name') . '=' . $db->q('com_sh404sef'));
			$db->setQuery($query)->query();
		}

		// 4.7.0+ now using Joomla url rewriting setting
		$status = $this->updateSettings();
		if (!$status)
		{
			JFactory::getApplication()
			        ->enqueuemessage('There was an error transferring or updating settings. You may have to update sh404SEF or Joomla configuration manually');
		}

		// message
		// decide on help file language
		$languageCode = Sh404sefHelperLanguage::getFamily();
		$basePath     = JPATH_ROOT . '/administrator/components/com_sh404sef/language/%s.postinstall.php';
		// fall back to english if language readme does not exist
		jimport('joomla.filesystem.file');
		if (!JFile::exists(sprintf($basePath, $languageCode)))
		{
			$languageCode = 'en';
		}

		ob_start();
		include sprintf($basePath, $languageCode);
		$postInstallMessage = ob_get_clean();
		JFactory::getApplication()->enqueueMessage($postInstallMessage);
	}

	private function _determineUtf8mb4Support()
	{
		$db = JFactory::getDbo();
		if (is_callable(array($db, 'hasUTF8mb4Support')))
		{
			if ($db->hasUTF8mb4Support())
			{
				$this->hasUtf8mb4Support = true;
				$this->charset           = 'utf8mb4';
				$this->collation         = 'utf8mb4_unicode_ci';
			}
		}
		if (!$this->hasUtf8mb4Support)
		{
			JFactory::getApplication()->enqueueMessage(
				'MYSQL server does not provide support for UTF8 MB4 character set. Database tables will not be using it.',
				'warning'
			);
		}
	}

	private function updateSettings()
	{
		$app = JFactory::getApplication();
		$j3  = version_compare(JVERSION, '3.0', 'ge');

		// get current (installed) sh404SEF settings
		try
		{
			if (!class_exists('Sh404sefFactory'))
			{
				// not previously installed, do nothing;
				return true;
			}
			// get the pre-existing config, the one instantiated before
			// installation started
			$sh404SEFConfig = Sh404sefFactory::getConfig();
			if (version_compare($sh404SEFConfig->version, '4.7.0', 'ge') || !$sh404SEFConfig->Enabled)
			{
				// already done, or not running, do nothing
				return true;
			}

			// installed and running, check/update Joomla config accordingly
			$joomlaUrlRewriting   = (bool) ($j3 ? $app->get('sef_rewrite') : $app->getCfg('sef_rewrite'));
			$sh404SEFUrlRewriting = (bool) $sh404SEFConfig->shRewriteMode;

			// opposite storage convention between Joomla and sh404SEF
			// so if settings are equal, then config is different
			if ($sh404SEFUrlRewriting == $joomlaUrlRewriting)
			{
				// different settings. sh404SEF is running and enabled, so sh404SEF setting
				// must replace the Joomla one
				$jConfig     = new JConfig();
				$jConfig     = JArrayHelper::fromObject($jConfig);
				$writeConfig = new JRegistry();
				$writeConfig->loadArray($jConfig);

				// replace Joomla config setting with ours
				$writeConfig->set('sef_rewrite', $sh404SEFUrlRewriting ? 0 : 1);

				// Set the configuration file path.
				$file = JPATH_CONFIGURATION . '/configuration.php';

				// Get the new FTP credentials.
				$ftp = JClientHelper::getCredentials('ftp', true);

				// Attempt to make the file writeable if using FTP.
				if (!$ftp['enabled'] && JPath::isOwner($file) && !JPath::setPermissions($file, '0644'))
				{
					$app->enqueueMessage('Error: Joomla! configuration.php file is not writable.', 'notice');
				}

				// Attempt to write the configuration file as a PHP class named JConfig.
				$configuration = $writeConfig->toString('PHP', array('class' => 'JConfig', 'closingtag' => false));

				if (!JFile::write($file, $configuration))
				{
					throw new RuntimeException('Error writing to Joomla! configuration.php file, maybe not writeable?');
				}
			}
		}
		catch (\Exception $e)
		{
			$app->enqueueMessage($e->getMessage());
			return false;
		}

		return true;
	}

	/**
	 * Decide whether backed up params should be restore (and
	 * plugins reinstalled).
	 * This should happen only when the extension is NOT already
	 * installed. Most of times, as we are using updagre install
	 * that should not happen and we jst overwrite
	 * but if user uninstalled the extension, we must restore
	 * data saved when he uninstalled
	 *
	 */
	private function _shouldRestore()
	{
		// IMPORTANT: the check is done once, and only once
		// as for later calls, the system plugin will have been installed
		// and thus the test will not be valid anymore
		static $restore = null;

		if (is_null($restore))
		{
			// search for base xml file to decide if already installed
			$restore = !JFile::exists(JPATH_ROOT . '/plugins/system/sh404sef/sh404sef.xml');
		}

		return $restore;
	}

	/**
	 * Install all sh404sef plugins available in a given
	 * group
	 *
	 * @param string $group name of group
	 *
	 * @return boolean, true if success
	 * @throws Exception
	 */
	private function _installPluginGroup($group)
	{
		$app = JFactory::getApplication();

		$sourcePath = JPATH_ADMINISTRATOR . '/components/com_sh404sef/plugins/' . $group;
		if (!JFolder::exists($sourcePath))
		{
			$app->enqueueMessage('Trying to install empty plugin group: ' . $group);

			return true;
		}

		// if each plugin resides in its own subDir, we must iterate over all sub dirs
		$folderList = JFolder::folders($sourcePath);
		if (empty($folderList))
		{
			$app->enqueueMessage('Trying to install empty plugin group, folder is empty: ' . $sourcePath);

			return true;
		}

		// process each plugin
		$errors = false;
		foreach ($folderList as $folder)
		{
			// install the plugin itself
			$status = $this->_installPlugin($group, $folder, $sourcePath);
			// set flag if an error happened, but keep installing
			// other plugins
			$errors = $errors || !$status;
			// also display status
			if (!$status)
			{
				$app->enqueueMessage('Error installing sh404sef plugin from ' . $folder);
			}
		}

		// return true if no error at all
		return $errors == false;
	}

	// V 1.2.4.t improved upgrading

	/**
	 * Insert in the db the previously retrieved parameters for a plugin
	 * including publication information. Also move files as required
	 *
	 * @param string $pluginFolder
	 * @param string $pluginElement
	 * @param        $sourcePath
	 *
	 * @return bool
	 * @throws Exception
	 */
	private function _installPlugin($pluginFolder, $pluginElement, $sourcePath)
	{
		if ($pluginFolder == 'system' && $pluginElement == 'shlib')
		{
			if (in_array('shlib', $this->_skipInstall))
			{
				return true;
			}
		}

		$status = true;
		$app    = JFactory::getApplication();

		// in case of upgrade, don't touch settings by user
		try
		{
			$db    = JFactory::getDbo();
			$query = $db->getQuery(true);
			$query->select('extension_id')->from('#__extensions')->where($db->qn('type') . '=' . $db->q('plugin'))
			      ->where($db->qn('element') . '=' . $db->q($pluginElement))
			      ->where($db->qn('folder') . '=' . $db->q($pluginFolder));
			$db->setQuery($query);
			$pluginId = $db->loadResult();
		}
		catch (\Exception $e)
		{
			$status = false;
			$app->enqueueMessage('Error reading pre-existing plugin record from db: ' . $pluginFolder . ' / ' . $pluginElement);

			return $status;
		}

		$overrides = empty($pluginId) ? array('ordering' => 10, 'enabled' => 1) : array();

		// make sure main library is loaded first
		if (empty($pluginId) && $pluginElement == 'shlib')
		{
			$overrides['ordering'] = -100;
		}

		// 4.19.0: new default option for sharing buttons plugin: use static buttons
		// must not change buttons type if already installed and setup
		if (!empty($pluginId) && $pluginElement == 'sh404sefsocial' && $pluginFolder == 'sh404sefcore')
		{
			$extension = JTable::getInstance('Extension');
			$extension->load($pluginId);
			$paramsJson = $extension->params;
			// create params object
			$paramsObject = new JRegistry();
			$paramsObject->loadString($paramsJson);
			// set buttonTypes param to "native"
			$currentButtonTypes = $paramsObject->get('buttonsType');
			if (empty($currentButtonTypes))
			{
				$paramsObject->set('buttonsType', 'native');
				// write back
				$extension->bind(
					array(
						'params' => $paramsObject->toString()
					)
				);
				$status = $extension->store();
				if (!$status)
				{
					$app->enqueueMessage('Error updating social sharing buttons options: ' . $extension->getError() . ' for ' . $pluginFolder . ' / ' . $pluginElement);
				}
			}
		}

		// use J! installer to fully install the plugin
		$installer = new JInstaller;
		$result    = $installer->install($sourcePath . '/' . $pluginElement);

		if ($result)
		{
			$shouldRestore = $this->_shouldRestore();

			if ($shouldRestore)
			{
				// read stored params from disk
				$saved = array();
				$this->_getExtensionSavedParams($pluginFolder . '.' . $pluginElement, $saved);
				unset($saved['extension_id']);
				$overrides = array_merge($overrides, $saved);
			}

			// overrides data in extension table, possibly overriding some columns from saved data
			if (!empty($overrides))
			{
				try
				{
					$query = $db->getQuery(true);
					$query->select('extension_id')->from('#__extensions')->where($db->qn('type') . '=' . $db->q('plugin'))
					      ->where($db->qn('element') . '=' . $db->q($pluginElement))
					      ->where($db->qn('folder') . '=' . $db->q($pluginFolder));
					$db->setQuery($query);
					$pluginId = $db->loadResult();
					$error    = $db->getErrorNum();
					if (!empty($error))
					{
						throw new Exception($db->getErrorMsg());
					}

					if (!empty($pluginId))
					{
						jimport('joomla.database.table.extension');
						$extension = JTable::getInstance('Extension');
						$extension->load($pluginId);
						$extension->bind($overrides);
						$status = $extension->store();
						if (!$status)
						{
							$app->enqueueMessage('Error writing updated extension record: ' . $extension->getError() . ' for ' . $pluginFolder . ' / ' . $pluginElement);
						}
					}
					else
					{
						$app->enqueueMessage('Error updating plugin DB record: ' . $pluginFolder . ' / ' . $pluginElement);
					}
				}
				catch (\Exception $e)
				{
					$status = false;
					$app->enqueueMessage('Error: ' . $e->getMessage());
				}
			}
		}
		else
		{
			$app->enqueueMessage('Error installing sh404sef plugin: ' . $pluginFolder . ' / ' . $pluginElement);
			$status = false;
		}

		return $status;
	}

	/**
	 * Makes sure the shLib, wbamp (and sh404SEF) system
	 * plugin is correct
	 */
	private function fixPluginsOrder()
	{
		try
		{
			// then make sure realtive order
			$this->ensurePluginsOrder('languagefilter', 'sh404sef')
			     ->ensurePluginsOrder('shlib', 'sh404sef');
		}
		catch (\Exception $e)
		{
			$app = JFactory::getApplication();
			$app->enqueueMessage('Error fixing plugin order in database: ' . $e->getMessage() . '. Please ensure the Language filter plugin is located before the sh404SEF system plugin.');
			return false;
		}

		return true;
	}

	private function ensurePluginsOrder($plugin1, $plugin2)
	{
		$order = $plugin1 . $plugin2;

		// Read current ordering
		$db    = JFactory::getDbo();
		$query = $db->getQuery(true);
		$query->select('*')->from('#__extensions')->where($db->qn('type') . '=' . $db->q('plugin'))
		      ->where($db->qn('folder') . '=' . $db->q('system'))
		      ->where($db->qn('element') . " in ('" . $plugin1 . "','" . $plugin2 . "')")
		      ->order('ordering asc');
		$db->setQuery($query);
		$plugins = $db->loadObjectList('element');

		// if one of the plugin is not there, don't go further
		if (empty($plugins[$plugin1]) || empty($plugins[$plugin2]))
		{
			return $this;
		}

		$signature = array_reduce(
			$plugins, function ($carry, $item) {
			return $carry . $item->element;
		}
		);

		if ($signature != $order || $plugins[$plugin2]->ordering == $plugins[$plugin1]->ordering)
		{
			// special case, same ordering
			if ($plugins[$plugin2]->ordering == $plugins[$plugin1]->ordering)
			{
				$plugins[$plugin1]->ordering += 1;
			}

			$query = $db->getQuery(true);
			// not in the right order, swap them
			$query->update($db->qn('#__extensions'))
			      ->set($db->qn('ordering') . '=' . $db->q($plugins[$plugin2]->ordering))
			      ->where($db->qn('extension_id') . '=' . $db->q($plugins[$plugin1]->extension_id));
			$db->setQuery($query)
			   ->query();
			$query = $db->getQuery(true);
			$query->update($db->qn('#__extensions'))
			      ->set($db->qn('ordering') . '=' . $db->q($plugins[$plugin1]->ordering))
			      ->where($db->qn('extension_id') . '=' . $db->q($plugins[$plugin2]->extension_id));
			$db->setQuery($query)
			   ->query();
		}

		return $this;
	}

	/**
	 * Retrieves stored params of a given extension (module or plugin)
	 * (as saved upon uninstall)
	 *
	 * @param string $extName the module name, including mod_ if a module
	 * @param array  $shConfig an array holding the database columns of the extension
	 * @param array  $shPub , an array holding the publication information of the module (only for modules)
	 *
	 * @return boolean, true if any stored parameters were found for this extension
	 */
	private function _getExtensionSavedParams($extName, &$shConfig, &$shPub = null, $useId = false)
	{
		static $fileList = array();

		// prepare default return value
		$status = false;

		// read all file names in /media/sh404_upgrade_conf dir, for easier processing
		$baseFolder = $this->_preserveConfigFolder . 'sh404_upgrade_conf';
		if (JFolder::exists($baseFolder) && (empty($fileList) || !isset($fileList[$extName])))
		{
			$baseName           = $extName . ($useId ? '_[0-9]{1,10}' : '') . '_' . $this->_siteId . '.php';
			$fileList[$extName] = JFolder::files($baseFolder, $baseName);
		}

		// extract filename from list we've established previously
		$extFile = isset($fileList[$extName]) && $fileList[$extName] !== false ? array_shift($fileList[$extName]) : '';
		if (empty($fileList[$extName]))
		{
			// prevent infinite loop
			$fileList[$extName] = false;
		}

		if (!empty($extFile) && JFile::exists($baseFolder . '/' . $extFile))
		{
			$status = true; // operation was successful
			include($baseFolder . '/' . $extFile);
		}

		return $status;
	}

	/**
	 *
	 * utility functions
	 *
	 */

	private function _installModule($module, $source, $extensionConfig, $moduleConfig)
	{
		$app = JFactory::getApplication();

		$path      = $source . '/admin/modules/' . $module;
		$installer = new JInstaller;
		$result    = $installer->install($path);

		if ($result)
		{
			// if files moved to destination, setup module in Joomla database
			$shouldRestore = $this->_shouldRestore();

			if ($shouldRestore)
			{
				// read stored params from disk
				$this->_getExtensionSavedParams($module . '_extension', $extensionConfig);
			}

			// update elements in db, only if we need to restore past configuration
			try
			{
				$db = JFactory::getDbo();
				if (!empty($extensionConfig))
				{
					// load module details from extension table
					//$moduleDetails = ShlDbHelper::selectAssoc('#__extensions', array('*'), array('type' => 'module', 'element' => $module));
					$query = $db->getQuery(true);
					$query->select('*')
					      ->from('#__extensions')
					      ->where($db->qn('type') . '=' . $db->q('module'))
					      ->where($db->qn('element') . '=' . $db->q($module));
					$db->setQuery($query);
					$moduleDetails = $db->loadAssoc();

					// merge with saved details and write back to disk
					$details = array_merge($moduleDetails, $extensionConfig);

					//ShlDbHelper::update('#__extensions', $details, array('extension_id' => (int) $moduleDetails['extension_id']));
					$query = $db->getQuery(true);
					$query->update('#__extensions')
					      ->where($db->qn('extension_id') . '=' . (int) $moduleDetails['extension_id']);
					foreach ($details as $key => $value)
					{
						$query->set($db->qn($key) . ' = ' . $db->q($value));
					}
					$db->setQuery($query)->query();
				}
			}
			catch (\Exception $e)
			{
				$app->enqueueMessage('Error: ' . $e->getMessage());
			}

			if ($shouldRestore)
			{
				// read stored params from disk
				$this->_getExtensionSavedParams($module . '_modules', $moduleConfig);
			}

			// update elements in db, if we need to restore past configuration
			try
			{
				//$instanceDetails = ShlDbHelper::selectAssoc('#__modules', array('*'), array('module' => $module));
				$query = $db->getQuery(true);
				$query->select('*')
				      ->from('#__modules')
				      ->where($db->qn('module') . '=' . $db->q($module));
				$db->setQuery($query);
				$instanceDetails = $db->loadAssoc();

				// merge with saved details and write back to disk
				$details = array_merge($instanceDetails, $moduleConfig);

				//ShlDbHelper::update('#__modules', $details, array('id' => (int) $instanceDetails['id']));
				$query = $db->getQuery(true);
				$query->update('#__modules')
				      ->where($db->qn('id') . '=' . (int) $instanceDetails['id']);
				foreach ($details as $key => $value)
				{
					$query->set($db->qn($key) . ' = ' . $db->q($value));
				}
				$db->setQuery($query)->query();
			}
			catch (\Exception $e)
			{
				$app->enqueueMessage('Error: ' . $e->getMessage());
			}

			// and finally we make sure there is a menu item associated with the module
			$details = array('menuid' => 0);

			if ($shouldRestore)
			{

				// read stored params from disk
				$this->_getExtensionSavedParams($module . '_modules_menu', $details);
			}
			$details = array_merge($details, array('moduleid' => (int) $instanceDetails['id']));

			// insert or update elements in db, if we need to restore past configuration
			try
			{
				//ShlDbHelper::insertUpdate('#__modules_menu', $details, array('moduleid' => (int) $instanceDetails['id']));

				$query = $db->getQuery(true);
				$query->select("count(*)")
				      ->from('#__modules_menu')
				      ->where($db->qn('moduleid') . '=' . (int) $instanceDetails['id']);
				$db->setQuery($query);
				$found = $db->loadResult();
				$query->clear();
				if (empty($found))
				{
					// insert
					$query->insert('#__modules_menu');
				}
				else
				{
					// update
					$query->update('#__modules_menu')
					      ->where($db->qn('moduleid') . '=' . (int) $instanceDetails['id']);
				}
				foreach ($details as $key => $value)
				{
					$query->set($db->qn($key) . ' = ' . $db->q($value));
				}
				$db->setQuery($query)->query();
			}
			catch (\Exception $e)
			{
				$app->enqueueMessage('Error: ' . $e->getMessage());
			}
		}
		else
		{
			$app->enqueueMessage('Error installing sh404sef module: ' . $module);
		}

		return $result;
	}

	/**
	 * As of 4.7.0, Analytics classic is removed, we only use Universal
	 * Transfer settings accordingly
	 */
	private function _updateAnalyticsAccount()
	{
		if (!class_exists('Sh404sefFactory'))
		{
			// sh404SEF not running, no copying over of
			// accounts. If user disabled sh404SEF system plugin
			// we can't really detect that, but then they can
			// manually copy over the analytics configuration
			return true;
		}

		$config = Sh404sefFactory::getConfig();
		try
		{
			$updated = false;
			$params  = Sh404sefHelperGeneral::getComponentParams();
			// select Universal if classic (or bot) was selected
			if (!empty($config->analyticsEdition) && ($config->analyticsEdition == 'ga' || $config->analyticsEdition == 'ga_and_uga'))
			{
				$params->set('analyticsEdition', 'uga');
				$updated = true;
			}

			// if we had a Classic web propery ID, move to UGA, unless we
			// already have such ID
			if (!empty($config->analyticsId) && empty($config->analyticsUgaId))
			{
				$params->set('analyticsUgaId', $config->analyticsId);
				$updated = true;
			}

			// if an
			if ($updated)
			{
				Sh404sefHelperGeneral::saveComponentParams($params);
			}
		}
		catch (\Exception $e)
		{
			$app = JFactory::getApplication();
			$app->enqueueMessage('Error: ' . $e->getMessage());

			return false;
		}

		return true;
	}

	/**
	 * In 4.7.1 and 4.7.2, it may happen that shURL collides with language codes
	 * on ML sites.
	 */
	private function _fixInvalidShurl()
	{
		if (!class_exists('Sh404sefFactory'))
		{
			// sh404SEF not running, do nothing
			// If user disabled sh404SEF system plugin
			// we can't really detect that, but then they can
			// manually copy over the analytics configuration
			return true;
		}

		try
		{
			// read all content languages, if any
			$languages = Sh404sefHelperLanguage::getInstalledLanguagesList();

			// adjust to require format
			$langCodes = array();
			if (!empty($languages))
			{
				foreach ($languages as $language)
				{
					$langCodes[] = $language->shortcode;
				}
			}

			// search for shURLs that match any of them
			if (!empty($langCodes))
			{
				ShlDbHelper::deleteIn('#__sh404sef_pageids', 'pageid', $langCodes);
			}
		}
		catch (\Exception $e)
		{
			$app = JFactory::getApplication();
			$app->enqueueMessage('Error: ' . $e->getMessage());

			return false;
		}

		return true;
	}

	/**
	 * In 4.12.0, canonical are now a type of aliases, instead of being managed as meta data. We must transfer existing
	 * canonical.
	 *
	 * @return bool
	 * @throws Exception
	 */
	private function _transferCanonical()
	{
		if (!class_exists('Sh404sefFactory'))
		{
			// sh404SEF not running, do nothing
			// If user disabled sh404SEF system plugin
			// we can't really detect that.
			return true;
		}

		// at this stage, needs to load table object ourselves
		include_once sh404SEF_ADMIN_ABS_PATH . 'tables/aliases.php';
		// re-include php_shortcuts, in case of installing over an older version
		// that may not have the latest version.
		include SHLIB_ROOT_PATH . 'system/php_shortcuts.php';

		try
		{
			// are there any canonical in the meta table?
			$canonicals = ShlDbHelper::selectObjectList(
				'#__sh404sef_metas',
				'*',
				'canonical != ""'
			);

			$db = JFactory::getDbo();

			// process canonicals
			if (!empty($canonicals))
			{
				$alias = array(
					'target_type' => 1
				);
				$root  = JUri::root();
				foreach ($canonicals as $canonical)
				{
					// insert in aliases table
					$alias['newurl']      = $canonical->canonical;
					$alias['alias']       = Sh404sefHelperGeneral::getSefFromNonSef($canonical->newurl);
					$alias['alias']       = wbLTrim($alias['alias'], $root);
					$alias['alias']       = wbLTrim($alias['alias'], 'administrator/');
					$alias['target_type'] = 1;
					$aliasObject          = new Sh404sefTableAliases($db);
					$aliasObject->bind(
						$alias
					);
					$aliasObject->store();

					// delete original
					ShlDbHelper::update(
						'#__sh404sef_metas',
						array(
							'canonical' => ''
						),
						array(
							'id' => $canonical->id
						)
					);
				}
			}
		}
		catch (\Exception $e)
		{
			$app = JFactory::getApplication();
			$app->enqueueMessage('Error: ' . $e->getMessage());

			return false;
		}

		return true;
	}

	private function _reorderAliases()
	{
		try
		{
			// are there any unordered aliases?
			$unorderedAliasesCount = ShlDbHelper::count(
				'#__sh404sef_aliases',
				'id',
				array(
					'ordering' => 0
				)
			);

			// if so, reorder them
			if (!empty($unorderedAliasesCount))
			{
				include_once sh404SEF_ADMIN_ABS_PATH . 'tables/aliases.php';
				$db = JFactory::getDbo();

				// make sure aliases are properly ordered
				$aliasesTable = new Sh404sefTableAliases($db);
				$aliasesTable->reorder();
			}
		}
		catch (\Exception $e)
		{
			$app = JFactory::getApplication();
			$app->enqueueMessage('Error: ' . $e->getMessage());

			return false;
		}

		return true;
	}

	/**
	 * Transfer to our keystore customized pages created by user, if any.
	 * Prior to 4.7.0, error pages were stored in regular Joomla articles.
	 *
	 * @param string $pageTitle
	 *
	 * @return bool|void
	 * @throws Exception
	 */
	private function _updateErrorPage($pageTitle = '__404__')
	{
		if (!class_exists('Sh404sefFactory'))
		{
			// sh404SEF not running, no copying over of
			// error page. If user disabled sh404SEF system plugin
			// we can't really detect that, but then they can
			// manually copy over the error page(s)
			return true;
		}

		// do we already have a __404__ article?
		$config = Sh404sefFactory::getConfig();
		try
		{
			$db    = JFactory::getDbo();
			$catid = $this->_getErrorPageCatId();
			$pages = array();
			if (!empty($this->_errorPageCatId))
			{
				// we have cat id, try to read article (only the published ones)
				// unpublished will be processed manually
				$query = $db->getQuery(true);
				$query->select('*')
				      ->from('#__content')
				      ->where($db->qn('catid') . '=' . $catid)
				      ->where($db->qn('title') . '=' . $db->q($pageTitle))
				      ->where($db->qn('state') . '= 1');
				$db->setQuery($query);
				$pages = $db->loadObjectList();
			}

			// if no pages had been created, go away
			// user may have renamed them for instance
			// manual operation needed or just do nothing
			if (empty($pages))
			{
				return true;
			}

			// there were some pages, copy them into keystore
			$defaultLanguageTag = JComponentHelper::getParams('com_languages')->get('site', 'en-GB');
			foreach ($pages as $page)
			{
				// "All" language page goes to default language record
				$currentLanguageTag = $page->language == '*' ? $defaultLanguageTag : $page->language;
				$storageKey         = 'com_sh404sef.errors.404.' . $currentLanguageTag;

				// is there already such an item?
				$query = $db->getQuery(true);
				$query->select('*')
				      ->from('#__sh404sef_keystore')
				      ->where($db->qn('key') . '=' . $db->q($storageKey));
				$db->setQuery($query);
				$storedPage = $db->loadObject();
				if (!empty($storedPage))
				{
					return;
				}

				// if none, insert
				$query = $db->getQuery(true);
				$query->insert($db->qn('#__sh404sef_keystore'));
				$query->set($db->qn('value') . '=' . $db->q(serialize($page->introtext)))
				      ->set($db->qn('key') . '=' . $db->q($storageKey))
				      ->set($db->qn('user_id') . '=' . JFactory::getUser()->id)
				      ->set($db->qn('format') . '= 0')
				      ->set($db->qn('modified_at') . '=' . $db->q(JFactory::getDate()->format('Y-m-d H:i:s')));
				$db->setQuery($query)->query();

				// lastly, trash the article
				$query = $db->getQuery(true);
				$query->update($db->qn('#__content'))
				      ->where($db->qn('id') . '=' . $db->q($page->id))
				      ->set($db->qn('state') . '= -2');
				$db->setQuery($query)->query();

				// was there a specific Itemid set?
				if (!empty($config->shPageNotFoundItemid))
				{
					$params    = Sh404sefHelperGeneral::getComponentParams();
					$fieldName = 'languages_' . $currentLanguageTag . '_notFoundItemid';
					$params->set($fieldName, $config->shPageNotFoundItemid);
					Sh404sefHelperGeneral::saveComponentParams($params);
				}
			}

			// and then trash the category
			$query = $db->getQuery(true);
			$query->update($db->qn('#__categories'))
			      ->where($db->qn('id') . '=' . $db->q($this->_errorPageCatId))
			      ->set($db->qn('published') . '= -2');
			$db->setQuery($query)->query();

			return true;
		}
		catch (\Exception $e)
		{
			$app = JFactory::getApplication();
			$app->enqueueMessage('Error: ' . $e->getMessage());

			return false;
		}
	}

	private function _getErrorPageCatId()
	{
		$db    = JFactory::getDbo();
		$query = $db->getQuery(true);
		$query->select('id')
		      ->from('#__categories')
		      ->where($db->qn('parent_id') . '= 1')
		      ->where($db->qn('extension') . '=' . $db->q('com_content'))
		      ->where($db->qn('path') . '=' . $db->q('sh404sef-custom-content'))
		      ->where($db->qn('level') . '= 1');
		$db->setQuery($query);
		$this->_errorPageCatId = $db->loadResult();

		// we have a category, all good
		return $this->_errorPageCatId;
	}

	private function _createDBStructure()
	{
		// get a db instance
		$this->_determineUtf8mb4Support();
		$db = JFactory::getDBO();

		$sqlQueries = array(

			"CREATE TABLE IF NOT EXISTS `#__sh404sef_urls` (
	`id` INT(11) NOT NULL AUTO_INCREMENT,
        `cpt` INT(11) NOT NULL DEFAULT '0',
        `rank` INT(11) NOT NULL DEFAULT '0',
        `oldurl` VARCHAR(2048) NOT NULL DEFAULT '',
        `newurl` VARCHAR(2048) NOT NULL DEFAULT '',
        `option` VARCHAR(255) NOT NULL DEFAULT '',
        `referrer_type` TINYINT(3) NOT NULL DEFAULT '0' COMMENT 'Used for 404, 0 = not set, 1 = external, 2 = internal',
        `dateadd` DATE NOT NULL DEFAULT '0000-00-00',
        `last_hit` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
        PRIMARY KEY (`id`),
        KEY `newurl` (`newurl` (190)),
        KEY `rank` (`rank`),
        KEY `oldurl` (`oldurl` (190)),
        KEY `last_hit` (`last_hit`)
        ) DEFAULT CHARSET=utf8;",

			"CREATE TABLE IF NOT EXISTS `#__sh404sef_metas` (
		`id` INT(11) NOT NULL AUTO_INCREMENT,
        `newurl` VARCHAR(2048) NOT NULL DEFAULT '',
        `metadesc` VARCHAR(512) CHARACTER SET " . $this->charset . " COLLATE " . $this->collation . " DEFAULT '',
        `metakey` VARCHAR(255) CHARACTER SET " . $this->charset . " COLLATE " . $this->collation . " DEFAULT  '',
        `metatitle` VARCHAR(255) CHARACTER SET " . $this->charset . " COLLATE " . $this->collation . " DEFAULT '',
        `metalang` VARCHAR(30) DEFAULT '',
        `metarobots` VARCHAR(30) DEFAULT '',
        `canonical` VARCHAR(2048) DEFAULT '',
        `og_enable` TINYINT(3) NOT NULL DEFAULT '2',
        `og_type` VARCHAR(30) DEFAULT '',
        `og_image` VARCHAR(255) DEFAULT '',
        `og_enable_description` TINYINT(3) NOT NULL DEFAULT '2',
        `og_enable_site_name` TINYINT(3) NOT NULL DEFAULT '2',
        `og_site_name` VARCHAR(255) DEFAULT '',
        `fb_admin_ids` VARCHAR(255) DEFAULT '',
        `og_enable_location` TINYINT(3) NOT NULL DEFAULT '2',
        `og_latitude` VARCHAR(30) DEFAULT '',
        `og_longitude` VARCHAR(30) DEFAULT '',
        `og_street_address` VARCHAR(255) DEFAULT '',
        `og_locality` VARCHAR(255) DEFAULT '',
        `og_postal_code` VARCHAR(30) DEFAULT '',
        `og_region` VARCHAR(255) DEFAULT '',
        `og_country_name` VARCHAR(255) DEFAULT '',
        `og_enable_contact` TINYINT(3) NOT NULL DEFAULT '2',
        `og_email` VARCHAR(255) DEFAULT '',
        `og_phone_number` VARCHAR(255) DEFAULT '',
        `og_fax_number` VARCHAR(255) DEFAULT '',
        `og_enable_fb_admin_ids` TINYINT(3) NOT NULL DEFAULT '2',
        `twittercards_enable` TINYINT(3) NOT NULL DEFAULT '2',
		`twittercards_site_account` VARCHAR(100) DEFAULT '',
		`twittercards_creator_account` VARCHAR(100) DEFAULT '',
		`google_authorship_enable` TINYINT(3) NOT NULL DEFAULT '2',
		`google_authorship_author_profile` VARCHAR(255) DEFAULT '',
		`google_authorship_author_name` VARCHAR(255) DEFAULT '',
		`google_publisher_enable` TINYINT(3) NOT NULL DEFAULT '2',
		`google_publisher_url` VARCHAR(255) DEFAULT '',
		`og_custom_description` VARCHAR(512) CHARACTER SET " . $this->charset . " COLLATE " . $this->collation . " DEFAULT '',
        `raw_content_head_top` MEDIUMTEXT CHARACTER SET " . $this->charset . " COLLATE " . $this->collation . " DEFAULT '',
        `raw_content_head_bottom` MEDIUMTEXT CHARACTER SET " . $this->charset . " COLLATE " . $this->collation . " DEFAULT '',
        `raw_content_body_top` MEDIUMTEXT CHARACTER SET " . $this->charset . " COLLATE " . $this->collation . " DEFAULT '',
        `raw_content_body_bottom` MEDIUMTEXT CHARACTER SET " . $this->charset . " COLLATE " . $this->collation . " DEFAULT '',
        PRIMARY KEY (`id`),
        KEY `newurl` (`newurl` (190))
        ) DEFAULT CHARSET=utf8;",

			"CREATE TABLE IF NOT EXISTS `#__sh404sef_aliases` (
	`id` INT(11) NOT NULL AUTO_INCREMENT,
        `newurl` VARCHAR(2048) NOT NULL DEFAULT '',
        `alias` VARCHAR(2048) NOT NULL DEFAULT '',
        `type` TINYINT(3) NOT NULL DEFAULT '0',
        `target_type` TINYINT(3) NOT NULL DEFAULT '0',
        `hits` INT(11) NOT NULL DEFAULT '0',
        `state` TINYINT(3) NOT NULL DEFAULT 1,
        `ordering` INT(11) NOT NULL DEFAULT 0,
        PRIMARY KEY (`id`),
        KEY `newurl` (`newurl` (190)),
        KEY `alias` (`alias` (190)),
        KEY `type` (`type`),
        KEY `state` (`state`)
        ) DEFAULT CHARSET=utf8;",

			"CREATE TABLE IF NOT EXISTS `#__sh404sef_pageids` (
	`id` INT(11) NOT NULL AUTO_INCREMENT,
        `newurl` VARCHAR(2048) NOT NULL DEFAULT '',
        `pageid` VARCHAR(255) NOT NULL DEFAULT '',
        `type` TINYINT(3) NOT NULL DEFAULT '0',
        `hits` INT(11) NOT NULL DEFAULT '0',
        PRIMARY KEY (`id`),
        KEY `newurl` (`newurl` (190)),
        KEY `alias` (`pageid` (190)),
        KEY `type` (`type`)
        ) DEFAULT CHARSET=" . $this->charset . ";",

			"CREATE TABLE IF NOT EXISTS `#__sh404sef_hits_shurls` (
	`id` INT(11) NOT NULL AUTO_INCREMENT,
        `url` VARCHAR(2048) NOT NULL DEFAULT '',
        `target` VARCHAR(2048) NOT NULL DEFAULT '',
        `target_domain` VARCHAR(191) NOT NULL DEFAULT '',
        `referrer` VARCHAR(2048) NOT NULL DEFAULT '',
        `referrer_domain` VARCHAR(191) NOT NULL DEFAULT '',
        `user_agent` VARCHAR(191) NOT NULL DEFAULT '',
        `ip_address` VARCHAR(50) NOT NULL DEFAULT '',
        `datetime` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
        `type` TINYINT(3) NOT NULL DEFAULT '0',
        PRIMARY KEY (`id`),
        KEY `url` (`url` (190)),
        KEY `referrer` (`referrer` (190)),
        KEY `user_agent` (`user_agent`),
        KEY `ip_address` (`ip_address`),
        KEY `type` (`type`)
        ) DEFAULT CHARSET=" . $this->charset . ";",

			"CREATE TABLE IF NOT EXISTS `#__sh404sef_hits_aliases` (
	`id` INT(11) NOT NULL AUTO_INCREMENT,
        `url` VARCHAR(2048) NOT NULL DEFAULT '',
        `target` VARCHAR(2048) NOT NULL DEFAULT '',
        `target_domain` VARCHAR(191) NOT NULL DEFAULT '',
        `referrer` VARCHAR(2048) NOT NULL DEFAULT '',
        `referrer_domain` VARCHAR(191) NOT NULL DEFAULT '',
        `user_agent` VARCHAR(191) NOT NULL DEFAULT '',
        `ip_address` VARCHAR(50) NOT NULL DEFAULT '',
        `datetime` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
        `type` TINYINT(3) NOT NULL DEFAULT '0',
        PRIMARY KEY (`id`),
        KEY `url` (`url` (190)),
        KEY `referrer` (`referrer` (190)),
        KEY `user_agent` (`user_agent`),
        KEY `ip_address` (`ip_address`),
        KEY `type` (`type`)
        ) DEFAULT CHARSET=" . $this->charset . ";",

			"CREATE TABLE IF NOT EXISTS `#__sh404sef_hits_404s` (
	`id` INT(11) NOT NULL AUTO_INCREMENT,
        `url` VARCHAR(2048) NOT NULL DEFAULT '',
        `target` VARCHAR(2048) NOT NULL DEFAULT '',
        `target_domain` VARCHAR(191) NOT NULL DEFAULT '',
        `referrer` VARCHAR(2048) NOT NULL DEFAULT '',
        `referrer_domain` VARCHAR(191) NOT NULL DEFAULT '',
        `user_agent` VARCHAR(191) NOT NULL DEFAULT '',
        `ip_address` VARCHAR(50) NOT NULL DEFAULT '',
        `datetime` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
        `type` TINYINT(3) NOT NULL DEFAULT '0',
        PRIMARY KEY (`id`),
        KEY `url` (`url` (190)),
        KEY `referrer` (`referrer` (190)),
        KEY `user_agent` (`user_agent`),
        KEY `ip_address` (`ip_address`),
        KEY `type` (`type`)
        ) DEFAULT CHARSET=" . $this->charset . ";",

			"CREATE TABLE IF NOT EXISTS `#__sh404sef_urls_src` (
	`id` INT(11) NOT NULL AUTO_INCREMENT,
        `url` VARCHAR(2048) NOT NULL DEFAULT '',
        `routed_url` VARCHAR(2048) NOT NULL DEFAULT '',
        `rank` INT(11) NOT NULL DEFAULT '0',
        `source_url` VARCHAR(2048) NOT NULL DEFAULT '',
        `source_routed_url` VARCHAR(2048) NOT NULL DEFAULT '',
        `trace` VARCHAR(7000) NOT NULL DEFAULT '',
        `datetime` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
        PRIMARY KEY (`id`),
        KEY `url` (`url` (190)),
        KEY `rank` (`rank`),
        KEY `routed_url` (`routed_url` (190)),
        KEY `source_url` (`source_url` (190))
        ) DEFAULT CHARSET=" . $this->charset . ";",

			"CREATE TABLE IF NOT EXISTS `#__sh404sef_keystore` (
	`id` INT(11) NOT NULL AUTO_INCREMENT,
		`scope` VARCHAR(50) NOT NULL DEFAULT 'default',
        `key` VARCHAR(191) NOT NULL DEFAULT '',
        `value` MEDIUMTEXT CHARACTER SET " . $this->charset . " COLLATE " . $this->collation . " NOT NULL,
        `user_id` INT NOT NULL DEFAULT 0,
        `format` TINYINT(3) NOT NULL DEFAULT 1,
        `modified_at` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
        PRIMARY KEY (`id`),
        KEY `main` (`scope`,`key`)
        ) ENGINE = InnoDB DEFAULT CHARSET=" . $this->charset . ";"
		);

		foreach ($sqlQueries as $query)
		{
			try
			{
				$db->setQuery($query);
				$db->query();
				$error = $db->getErrorNum();
				if (!empty($error))
				{
					throw new Exception($db->getErrorMsg());
				}
			}
			catch (\Exception $e)
			{
				$app = JFactory::getApplication();
				$app
					->enqueueMessage(
						'Error while creating the database : ' . $e->getMessage()
						. '. Sh404SEF will probably not operate properly. Please uninstall it, then try again after checking your database server setup. Contact us in case this happens again.'
					);
			}
		}
	}

	/**
	 * Performs update to db stucture on existing setups
	 */
	private function _updateDBStructure()
	{
		$this->_determineUtf8mb4Support();
		// get a db instance
		$db = JFactory::getDBO();

		$subQueries = array();

		// when upgrading from older version, these values could be missing at the time of install
		defined('SH404SEF_OPTION_VALUE_NO') or define('SH404SEF_OPTION_VALUE_NO', 0);
		defined('SH404SEF_OPTION_VALUE_YES') or define('SH404SEF_OPTION_VALUE_YES', 1);
		defined('SH404SEF_OPTION_VALUE_USE_DEFAULT') or define('SH404SEF_OPTION_VALUE_USE_DEFAULT', 2);

		// ------------------------------------------------------------------------------
		// #__sh404sef_urls
		// ------------------------------------------------------------------------------
		$subqueries['#__sh404sef_urls'] = array();
		if (method_exists($db, 'getTableFields'))
		{
			$columns = $db->getTableFields('#__sh404sef_urls');
			$columns = empty($columns['#__sh404sef_urls']) ? array() : $columns['#__sh404sef_urls'];
		}
		else
		{
			$columns = $db->getTableColumns('#__sh404sef_urls');
		}

		// 4.7.0 new columns for URLs table
		if (empty($columns['option']))
		{
			$subQueries['#__sh404sef_urls'][] = "add `option` varchar(255) NOT NULL DEFAULT ''";
		}
		if (empty($columns['referrer_type']))
		{
			$subQueries['#__sh404sef_urls'][] = "add `referrer_type` tinyint(3) NOT NULL DEFAULT '" . SH404SEF_OPTION_VALUE_NO . "'";
		}

		// upgrading to 4.7.0, we reset the hit counter of regular URLs.
		// previous counts may/will be wrong because they are the result of 404s
		// (for which hits were counted) turned into valid SEF (for which, up to now)
		// hits were not counted.
		// Thus we set to 0 all `cpt` fields for non-404 URLS (ie: automatic and custom).
		// of course, we must do this only once. We detect this condition by looking for
		// the referrer_type column in the table, which was introduced at the same time
		// as the hit counting feature
		if (empty($columns['referrer_type']))
		{
			try
			{
				$query = 'update ' . $db->qn('#__sh404sef_urls') . ' set ' . $db->qn('cpt') . ' = 0'
					. ' where ' . $db->qn('newurl') . " <> ''";
				$db->setQuery($query);
				$db->query();
				$error = $db->getErrorNum();
				if (!empty($error))
				{
					throw new Exception($db->getErrorMsg());
				}
			}
			catch (\Exception $e)
			{
				JFactory::getApplication()
				        ->enqueueMessage(
					        'Error while upgrading the database : ' . $e->getMessage()
					        . '. Sh404SEF will probably not operate properly. Please uninstall it, then try again after checking your database server setup. Contact us in case this happens again.'
				        );
			}
		}

		// 4.8.0 added last_hit column
		if (empty($columns['last_hit']))
		{
			$subQueries['#__sh404sef_urls'][] = "add `last_hit` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00'";
			$subQueries['#__sh404sef_urls'][] = "add index `last_hit` (`last_hit`)";
		}

		// 4.13.3 -  extend all URL fields to 2048 chars
		$subQueries['#__sh404sef_urls'][] = 'modify `oldurl` varchar(2048)';
		$subQueries['#__sh404sef_urls'][] = 'DROP INDEX `oldurl`';
		$subQueries['#__sh404sef_urls'][] = 'add index `oldurl` (`oldurl`(190))';
		$subQueries['#__sh404sef_urls'][] = 'modify `newurl` varchar(2048)';
		$subQueries['#__sh404sef_urls'][] = 'DROP INDEX `newurl`';
		$subQueries['#__sh404sef_urls'][] = 'add index `newurl` (`newurl`(190))';

		// ------------------------------------------------------------------------------
		// #__sh404sef_metas
		// ------------------------------------------------------------------------------
		// get list of columns
		// platform 12.1
		if (method_exists($db, 'getTableFields'))
		{
			$columns = $db->getTableFields('#__sh404sef_metas');
			$columns = empty($columns['#__sh404sef_metas']) ? array() : $columns['#__sh404sef_metas'];
		}
		else
		{
			$columns = $db->getTableColumns('#__sh404sef_metas');
		}

		$subqueries['#__sh404sef_metas'] = array();
		if (empty($columns['canonical']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `canonical` varchar(2048) default ''";
		}

		if (empty($columns['og_enable']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `og_enable` tinyint(3) NOT NULL DEFAULT '" . SH404SEF_OPTION_VALUE_USE_DEFAULT . "'";
		}
		if (empty($columns['og_type']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `og_type` varchar(30) default ''";
		}
		if (empty($columns['og_image']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `og_image` varchar(255) default ''";
		}
		if (empty($columns['og_enable_description']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `og_enable_description` tinyint(3) NOT NULL DEFAULT '" . SH404SEF_OPTION_VALUE_USE_DEFAULT . "'";
		}
		if (empty($columns['og_enable_site_name']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `og_enable_site_name` tinyint(3) NOT NULL DEFAULT '" . SH404SEF_OPTION_VALUE_USE_DEFAULT . "'";
		}
		if (empty($columns['og_site_name']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `og_site_name` varchar(255) default ''";
		}
		if (empty($columns['fb_admin_ids']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `fb_admin_ids` varchar(255) default ''";
		}
		if (empty($columns['og_enable_location']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `og_enable_location` tinyint(3) NOT NULL DEFAULT '" . SH404SEF_OPTION_VALUE_USE_DEFAULT . "'";
		}
		if (empty($columns['og_latitude']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `og_latitude` varchar(30) default ''";
		}
		if (empty($columns['og_longitude']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `og_longitude` varchar(30) default ''";
		}
		if (empty($columns['og_street_address']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `og_street_address` varchar(255) default ''";
		}
		if (empty($columns['og_locality']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `og_locality` varchar(255) default ''";
		}
		if (empty($columns['og_postal_code']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `og_postal_code` varchar(30) default ''";
		}
		if (empty($columns['og_region']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `og_region` varchar(255) default ''";
		}
		if (empty($columns['og_country_name']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `og_country_name` varchar(255) default ''";
		}
		if (empty($columns['og_enable_contact']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `og_enable_contact` tinyint(3) NOT NULL DEFAULT '" . SH404SEF_OPTION_VALUE_USE_DEFAULT . "'";
		}
		if (empty($columns['og_email']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `og_email` varchar(255) default ''";
		}
		if (empty($columns['og_phone_number']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `og_phone_number` varchar(255) default ''";
		}
		if (empty($columns['og_fax_number']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `og_fax_number` varchar(255) default ''";
		}
		if (empty($columns['og_enable_fb_admin_ids']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `og_enable_fb_admin_ids` tinyint(3) NOT NULL DEFAULT '" . SH404SEF_OPTION_VALUE_USE_DEFAULT . "'";
		}

		// twitter cards
		if (empty($columns['twittercards_enable']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `twittercards_enable` tinyint(3) NOT NULL DEFAULT '" . SH404SEF_OPTION_VALUE_USE_DEFAULT . "'";
		}
		if (empty($columns['twittercards_site_account']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `twittercards_site_account` varchar(100) default ''";
		}
		if (empty($columns['twittercards_creator_account']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `twittercards_creator_account` varchar(100) default ''";
		}

		// google authorship
		if (empty($columns['google_authorship_enable']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `google_authorship_enable` tinyint(3) NOT NULL DEFAULT '" . SH404SEF_OPTION_VALUE_USE_DEFAULT . "'";
		}
		if (empty($columns['google_authorship_author_profile']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `google_authorship_author_profile` varchar(255) default ''";
		}
		if (empty($columns['google_authorship_author_name']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `google_authorship_author_name` varchar(255) default ''";
		}

		// google publisher
		if (empty($columns['google_publisher_enable']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `google_publisher_enable` tinyint(3) NOT NULL DEFAULT '" . SH404SEF_OPTION_VALUE_USE_DEFAULT . "'";
		}
		if (empty($columns['google_publisher_url']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `google_publisher_url` varchar(255) default ''";
		}

		// 4.10.1 allow utf8mb4
		if ($this->hasUtf8mb4Support && class_exists('Sh404sefFactory') && version_compare(Sh404sefFactory::getConfig()->version, '4.10.1', 'lt'))
		{
			// updating
			$subQueries['#__sh404sef_metas'][] = 'modify `metatitle` varchar(255) CHARACTER SET ' . $this->charset . ' collate ' . $this->collation . '';
			$subQueries['#__sh404sef_metas'][] = 'modify `metakey` varchar(255) CHARACTER SET ' . $this->charset . ' collate ' . $this->collation . '';
		}

		// 4.13.0: expanded max meta description length from 255 to 512 chars
		$subQueries['#__sh404sef_metas'][] = 'modify `metadesc` varchar(512) CHARACTER SET ' . $this->charset . ' collate ' . $this->collation . '';

		// 4.13.3 -  extend all URL fields to 2048 chars
		$subQueries['#__sh404sef_metas'][] = 'modify `newurl` varchar(2048)';
		$subQueries['#__sh404sef_metas'][] = 'DROP INDEX `newurl`';
		$subQueries['#__sh404sef_metas'][] = 'add index `newurl` (`newurl`(190))';

		// 4.17.1 - add custom OGP description
		if (empty($columns['og_custom_description']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `og_custom_description` varchar(512) CHARACTER SET " . $this->charset . " collate " . $this->collation . " default ''";
		}
		else
		{
			$subQueries['#__sh404sef_metas'][] = "modify `og_custom_description` varchar(512) CHARACTER SET " . $this->charset . " collate " . $this->collation;
		}
		// add custom head/body raw content per URL
		if (empty($columns['raw_content_head_top']))
		{
			$subQueries['#__sh404sef_metas'][] = "add `raw_content_head_top` MEDIUMTEXT CHARACTER SET " . $this->charset . " COLLATE " . $this->collation . " DEFAULT ''";
			$subQueries['#__sh404sef_metas'][] = "add `raw_content_head_bottom` MEDIUMTEXT CHARACTER SET " . $this->charset . " COLLATE " . $this->collation . " DEFAULT ''";
			$subQueries['#__sh404sef_metas'][] = "add `raw_content_body_top` MEDIUMTEXT CHARACTER SET " . $this->charset . " COLLATE " . $this->collation . " DEFAULT ''";
			$subQueries['#__sh404sef_metas'][] = "add `raw_content_body_bottom` MEDIUMTEXT CHARACTER SET " . $this->charset . " COLLATE " . $this->collation . " DEFAULT ''";
		}
		else
		{
			$subQueries['#__sh404sef_metas'][] = "modify `raw_content_head_top` MEDIUMTEXT CHARACTER SET " . $this->charset . " COLLATE " . $this->collation . " DEFAULT ''";
			$subQueries['#__sh404sef_metas'][] = "modify `raw_content_head_bottom` MEDIUMTEXT CHARACTER SET " . $this->charset . " COLLATE " . $this->collation . " DEFAULT ''";
			$subQueries['#__sh404sef_metas'][] = "modify `raw_content_body_top` MEDIUMTEXT CHARACTER SET " . $this->charset . " COLLATE " . $this->collation . " DEFAULT ''";
			$subQueries['#__sh404sef_metas'][] = "modify `raw_content_body_bottom` MEDIUMTEXT CHARACTER SET " . $this->charset . " COLLATE " . $this->collation . " DEFAULT ''";
		}
		// ------------------------------------------------------------------------------
		// #__sh404sef_aliases
		// ------------------------------------------------------------------------------
		$subqueries['#__sh404sef_aliases'] = array();
		if (method_exists($db, 'getTableFields'))
		{
			$columns = $db->getTableFields('#__sh404sef_aliases');
			$columns = empty($columns['#__sh404sef_aliases']) ? array() : $columns['#__sh404sef_aliases'];
		}
		else
		{
			$columns = $db->getTableColumns('#__sh404sef_aliases');
		}

		// some very old install don't have the 'type' or 'hits' columns in aliases table
		if (empty($columns['type']))
		{
			$subQueries['#__sh404sef_aliases'][] = "add `type` tinyint(3) NOT NULL DEFAULT '" . SH404SEF_OPTION_VALUE_NO . "'";
			$subQueries['#__sh404sef_aliases'][] = "add index `type` (`type`)";
		}

		// hits type
		if (empty($columns['hits']))
		{
			$subQueries['#__sh404sef_aliases'][] = "add `hits` int(11) NOT NULL DEFAULT '" . SH404SEF_OPTION_VALUE_NO . "'";
		}

		// 4.12
		if (empty($columns['target_type']))
		{
			$subQueries['#__sh404sef_aliases'][] = "add `target_type` tinyint(3) NOT NULL DEFAULT '" . SH404SEF_OPTION_VALUE_NO . "'";
			$subQueries['#__sh404sef_aliases'][] = "add index `target_type` (`target_type`)";
		}
		if (empty($columns['state']))
		{
			$subQueries['#__sh404sef_aliases'][] = "add `state` tinyint(3) NOT NULL DEFAULT 1";
			$subQueries['#__sh404sef_aliases'][] = "add index `state` (`state`)";
		}
		if (empty($columns['ordering']))
		{
			$subQueries['#__sh404sef_aliases'][] = "add `ordering` int(11) NOT NULL DEFAULT 0";
		}

		// 4.13.3 -  extend all URL fields to 2048 chars
		$subQueries['#__sh404sef_aliases'][] = 'modify `newurl` varchar(2048)';
		$subQueries['#__sh404sef_aliases'][] = 'DROP INDEX `newurl`';
		$subQueries['#__sh404sef_aliases'][] = 'add index `newurl` (`newurl`(190))';
		$subQueries['#__sh404sef_aliases'][] = 'modify `alias` varchar(2048)';
		$subQueries['#__sh404sef_aliases'][] = 'DROP INDEX `alias`';
		$subQueries['#__sh404sef_aliases'][] = 'add index `alias` (`alias`(190))';

		// ------------------------------------------------------------------------------
		// #__sh404sef_pageids
		// ------------------------------------------------------------------------------

		$subQueries['#__sh404sef_pageids'] = array();

		// 4.12.0 pageids/shurl allow external target, expand target URL column, but limit index
		if ($this->hasUtf8mb4Support && class_exists('Sh404sefFactory') && version_compare(Sh404sefFactory::getConfig()->version, '4.12.0', 'lt'))
		{
			$subQueries['#__sh404sef_pageids'][] = 'DROP INDEX `alias`';
			$subQueries['#__sh404sef_pageids'][] = 'add index `alias` (`pageid`(190))';
		}

		// 4.13.3 -  extend all URL fields to 2048 chars
		$subQueries['#__sh404sef_pageids'][] = 'modify `newurl` varchar(2048)';
		$subQueries['#__sh404sef_pageids'][] = 'DROP INDEX `newurl`';
		$subQueries['#__sh404sef_pageids'][] = 'add index `newurl` (`newurl`(190))';

		// ------------------------------------------------------------------------------
		// #__sh404sef_hits_shurls
		// ------------------------------------------------------------------------------

		$subQueries['#__sh404sef_hits_shurls'] = array();

		// 4.13.3 -  extend all URL fields to 2048 chars
		$subQueries['#__sh404sef_hits_shurls']   = array();
		$subQueries['#__sh404sef_hits_shurls'][] = 'modify `url` varchar(2048)';
		$subQueries['#__sh404sef_hits_shurls'][] = 'DROP INDEX `url`';
		$subQueries['#__sh404sef_hits_shurls'][] = 'add index `url` (`url`(190))';
		$subQueries['#__sh404sef_hits_shurls'][] = 'modify `target` varchar(2048)';
		$subQueries['#__sh404sef_hits_shurls'][] = 'modify `referrer` varchar(2048)';
		$subQueries['#__sh404sef_hits_shurls'][] = 'DROP INDEX `referrer`';
		$subQueries['#__sh404sef_hits_shurls'][] = 'add index `referrer` (`referrer`(190))';

		// ------------------------------------------------------------------------------
		// #__sh404sef_hits_aliases
		// ------------------------------------------------------------------------------
		$subQueries['#__sh404sef_hits_aliases'] = array();

		// 4.13.3 -  extend all URL fields to 2048 chars
		$subQueries['#__sh404sef_hits_aliases']   = array();
		$subQueries['#__sh404sef_hits_aliases'][] = 'modify `url` varchar(2048)';
		$subQueries['#__sh404sef_hits_aliases'][] = 'DROP INDEX `url`';
		$subQueries['#__sh404sef_hits_aliases'][] = 'add index `url` (`url`(190))';
		$subQueries['#__sh404sef_hits_aliases'][] = 'modify `target` varchar(2048)';
		$subQueries['#__sh404sef_hits_aliases'][] = 'modify `referrer` varchar(2048)';
		$subQueries['#__sh404sef_hits_aliases'][] = 'DROP INDEX `referrer`';
		$subQueries['#__sh404sef_hits_aliases'][] = 'add index `referrer` (`referrer`(190))';

		// ------------------------------------------------------------------------------
		// #__sh404sef_hits_404s
		// ------------------------------------------------------------------------------
		$subQueries['#__sh404sef_hits_404s'] = array();

		// 4.13.3 -  extend all URL fields to 2048 chars
		$subQueries['#__sh404sef_hits_404s']   = array();
		$subQueries['#__sh404sef_hits_404s'][] = 'modify `url` varchar(2048)';
		$subQueries['#__sh404sef_hits_404s'][] = 'DROP INDEX `url`';
		$subQueries['#__sh404sef_hits_404s'][] = 'add index `url` (`url`(190))';
		$subQueries['#__sh404sef_hits_404s'][] = 'modify `target` varchar(2048)';
		$subQueries['#__sh404sef_hits_404s'][] = 'modify `referrer` varchar(2048)';
		$subQueries['#__sh404sef_hits_404s'][] = 'DROP INDEX `referrer`';
		$subQueries['#__sh404sef_hits_404s'][] = 'add index `referrer` (`referrer`(190))';

		// ------------------------------------------------------------------------------
		// #__sh404sef_urls_src
		// ------------------------------------------------------------------------------

		if (method_exists($db, 'getTableFields'))
		{
			$columns = $db->getTableFields('#__sh404sef_urls_src');
			$columns = empty($columns['#__sh404sef_urls_src']) ? array() : $columns['#__sh404sef_urls_src'];
		}
		else
		{
			$columns = $db->getTableColumns('#__sh404sef_urls_src');
		}

		// 4.13.2 URL src also stores URL rank
		$subqueries['#__sh404sef_urls_src'] = array();
		if (empty($columns['rank']))
		{
			$subQueries['#__sh404sef_urls_src'][] = "add `rank` INT(11) NOT NULL DEFAULT '0'";
			$subQueries['#__sh404sef_urls_src'][] = 'add index `rank` (`rank`)';
		}

		// 4.13.3 -  extend all URL fields to 2048 chars
		$subQueries['#__sh404sef_urls_src'][] = 'modify `url` varchar(2048)';
		$subQueries['#__sh404sef_urls_src'][] = 'DROP INDEX `url`';
		$subQueries['#__sh404sef_urls_src'][] = 'add index `url` (`url`(190))';
		$subQueries['#__sh404sef_urls_src'][] = 'modify `routed_url` varchar(2048)';
		$subQueries['#__sh404sef_urls_src'][] = 'DROP INDEX `routed_url`';
		$subQueries['#__sh404sef_urls_src'][] = 'add index `routed_url` (`routed_url`(190))';
		$subQueries['#__sh404sef_urls_src'][] = 'modify `source_url` varchar(2048)';
		$subQueries['#__sh404sef_urls_src'][] = 'DROP INDEX `source_url`';
		$subQueries['#__sh404sef_urls_src'][] = 'add index `source_url` (`source_url`(190))';
		$subQueries['#__sh404sef_urls_src'][] = 'modify `source_routed_url` varchar(2048)';
		$subQueries['#__sh404sef_urls_src'][] = 'modify `trace` varchar(7000)';

		// ------------------------------------------------------------------------------
		// apply changes
		// ------------------------------------------------------------------------------
		if (!empty($subQueries))
		{
			try
			{
				foreach ($subQueries as $table => $queries)
				{
					// aggregate sub-queries
					$queries = implode(', ', $queries);

					// prepend query
					$query = 'ALTER TABLE ' . $db->qn($table) . ' ' . $queries;

					// run query
					$db->setQuery($query);
					$db->query();
					$error = $db->getErrorNum();
					if (!empty($error))
					{
						throw new Exception($db->getErrorMsg());
					}
				}
			}
			catch (\Exception $e)
			{
				JFactory::getApplication()
				        ->enqueueMessage(
					        'Error while upgrading the database : ' . $e->getMessage()
					        . '. Sh404SEF will probably not operate properly. Please uninstall it, then try again after checking your database server setup. Contact us in case this happens again.'
				        );
			}
		}
	}

	/**
	 * Make sure the update site is correct when switching from
	 * one edition to another.
	 * Specifically, wipe out update site when going from an
	 * edition that does auto-update to one that does not
	 * (ie community to full or lite)
	 *
	 * @param unknown $type
	 * @param unknown $parent
	 *
	 * @return boolean
	 */
	private function _processUpdateSite($type, $parent)
	{
		// figure out the extension id
		try
		{
			//$extensionId = (int) ShlDbHelper::selectResult('#__extensions', array('extension_id'),
			//	array('type' => 'component', 'element' => 'com_sh404sef'));
			$db    = JFactory::getDbo();
			$query = $db->getQuery(true);
			$query->select('extension_id')
			      ->from('#__extensions')
			      ->where($db->qn('type') . ' = ' . $db->q('component'))
			      ->where($db->qn('element') . ' = ' . $db->q('com_sh404sef'));
			$db->setQuery($query);
			$extensionId = $db->loadResult();
		}
		catch (\Exception $e)
		{
			$extensionId = 0;
		}
		// no update site. We must make sure we wipe out
		// any existing update site. We use Joomla code for that
		if (!empty($extensionId))
		{
			JPluginHelper::importPlugin('extension');
			if (version_compare(JVERSION, '3', 'ge'))
			{
				$dispatcher = JEventDispatcher::getInstance();
			}
			else
			{
				$dispatcher = JDispatcher::getInstance();
			}
			// Fire the onExtensionAfterInstall
			$result = null;
			$dispatcher->trigger('onExtensionAfterUninstall', array('installer' => clone $parent, 'eid' => $extensionId, 'result' => $result));
		}

		return true;
	}

}

Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists