<?php
/**
* @package		Komento
* @copyright	Copyright (C) Stack Ideas Sdn Bhd. All rights reserved.
* @license		GNU/GPL, see LICENSE.php
* Komento is free software. This version may have been modified pursuant
* to the GNU General Public License, and as distributed it includes or
* is derivative of works licensed under the GNU General Public License or
* other free or open source software licenses.
* See COPYRIGHT.php for copyright notices and details.
*/
use Joomla\CMS\Language\Text;
use Joomla\CMS\Installer\Installer;
use Joomla\CMS\Filesystem\Folder;

defined('_JEXEC') or die('Unauthorized Access');

require_once(dirname(__FILE__) . '/controller.php');

class KomentoControllerAddonsInstallModule extends KomentoSetupController
{
	public function execute()
	{
		$this->engine();

		// Get a list of folders in the module and plugins.
		$path = $this->input->get('path', '', 'default');

		// Determines which module to install on the site
		$module = $this->input->get('module', '', 'cmd');

		// Construct the absolute path to the module
		$absolutePath = $path . '/' . $module;

		// Try to install the module now.
		$state = $this->install($module, $absolutePath);

		$this->setInfo(Text::sprintf('Module %1$s installed on the site', $module), true);
		return $this->output();
	}

	public function install($element, $path)
	{
		$db = KT::db();

		// Snapshot existing module instance IDs before install, so we can tell
		// apart the customer's pre-existing instances from any duplicate row
		// Joomla's installer creates as a side-effect of an update run.
		$existingIds = $this->getExistingModuleIds($element);
		$isUpdate = !empty($existingIds);

		// Get Joomla's installer instance
		$installer = Installer::getInstance();

		// Allow overwriting existing modules
		$installer->setOverwrite(true);

		// Install the module
		$state = $installer->install($path);

		if (!$state) {
			return false;
		}

		// For existing modules: always explicitly copy all files to the live module
		// directory. Joomla's installer may skip or fail file overwriting during
		// update runs, leaving customers with stale module code from earlier installs.
		if ($isUpdate) {
			$target = JPATH_ROOT . '/modules/' . $element;

			if (!Folder::exists($target)) {
				Folder::create($target);
			}

			Folder::copy($path, $target, '', true);
		}

		$query = array();
		$query[] = 'UPDATE ' . $db->qn('#__extensions') . ' SET ' . $db->qn('access') . '=' . $db->Quote(1);
		$query[] = 'WHERE ' . $db->qn('type') . '=' . $db->Quote('module');
		$query[] = 'AND ' . $db->qn('element') . '=' . $db->Quote($element);
		$query[] = 'AND ' . $db->qn('access') . '=' . $db->Quote(0);

		$query = implode(' ', $query);

		$db->setQuery($query);
		$db->execute();

		if ($isUpdate) {
			// Update run: remove any new #__modules rows Joomla created as a
			// side-effect, leaving every pre-existing customer instance untouched.
			$this->removeNewModuleInstances($element, $existingIds);

			return true;
		}

		// Fresh install: assign modules that have no menu entry yet.
		$query = array();
		$query[] = 'SELECT a.' . $db->qn('id') . ', b.' . $db->qn('moduleid') . ' FROM ' . $db->qn('#__modules') . ' AS a';
		$query[] = 'LEFT JOIN ' . $db->qn('#__modules_menu') . ' AS b ON a.' . $db->qn('id') . ' = b.' . $db->qn('moduleid');
		$query[] = 'WHERE a.' . $db->qn('module') . ' = ' . $db->Quote($element);
		$query[] = 'AND b.' . $db->qn('moduleid') . ' IS NULL';

		$query = implode(' ', $query);
		$db->setQuery($query);

		$result = $db->loadObjectList();

		if (!$result) {
			return false;
		}

		foreach ($result as $row) {
			$mod = new stdClass();
			$mod->moduleid = $row->id;
			$mod->menuid = 0;

			$db->insertObject('#__modules_menu', $mod);
		}

		return true;
	}

	/**
	 * Returns all existing #__modules row IDs for a given module element name.
	 *
	 * @since	5.0.2
	 * @access	private
	 */
	private function getExistingModuleIds($element)
	{
		$db = KT::db();

		$query = 'SELECT ' . $db->qn('id') . ' FROM ' . $db->qn('#__modules') . ' WHERE ' . $db->qn('module') . '=' . $db->Quote($element);

		$db->setQuery($query);

		return $db->loadColumn() ?: [];
	}

	/**
	 * Removes #__modules (and #__modules_menu) rows that were not present before
	 * the current installation run - i.e. duplicate rows Joomla's installer creates
	 * on update. Pre-existing customer instances are never touched.
	 *
	 * @since	5.0.2
	 * @access	private
	 */
	private function removeNewModuleInstances($element, $existingIds)
	{
		$db = KT::db();

		$query = 'SELECT ' . $db->qn('id') . ' FROM ' . $db->qn('#__modules') . ' WHERE ' . $db->qn('module') . '=' . $db->Quote($element);

		$db->setQuery($query);
		$currentIds = $db->loadColumn() ?: [];

		$newIds = array_diff($currentIds, $existingIds);

		if (empty($newIds)) {
			return;
		}

		$idList = implode(',', array_map('intval', $newIds));

		$db->setQuery('DELETE FROM ' . $db->qn('#__modules_menu') . ' WHERE ' . $db->qn('moduleid') . ' IN (' . $idList . ')');
		$db->execute();

		$db->setQuery('DELETE FROM ' . $db->qn('#__modules') . ' WHERE ' . $db->qn('id') . ' IN (' . $idList . ')');
		$db->execute();
	}
}
