<?php
/* ======================================================
 # Login as User for Joomla! - v3.5.9 (pro version)
 # -------------------------------------------------------
 # For Joomla! CMS (v3.x)
 # Author: Web357 (Yiannis Christodoulou)
 # Copyright (©) 2014-2022 Web357. All rights reserved.
 # License: GNU/GPLv3, http://www.gnu.org/licenses/gpl-3.0.html
 # Website: https:/www.web357.com
 # Demo: https://demo.web357.com/joomla/login-as-user
 # Support: support@web357.com
 # Last modified: Wednesday 07 December 2022, 11:05:26 AM
 ========================================================= */
defined('_JEXEC') or die;

use Joomla\CMS\Factory;
use Joomla\CMS\Uri\Uri;
use Joomla\Utilities\ArrayHelper;
use Joomla\CMS\Language\Text;

jimport('joomla.plugin.plugin');

class plgSystemLoginAsUser extends JPlugin
{
	public function onAfterInitialise()
	{
		if (JDEBUG)
		{
			JLog::addLogger(array('text_file' => 'plg_system_loginasuser.log.php'), JLog::ALL, array('plg_system_loginasuser'));
		}

		jimport('joomla.environment.uri' );
		$db = JFactory::getDBO();
		$host = JURI::root();
		$option = JFactory::getApplication()->input->get('option', '', 'STRING');
		$ctrl = JFactory::getApplication()->input->get('ctrl', '', 'STRING');
		$view = JFactory::getApplication()->input->get('view', '', 'STRING');
		
		// CSS - backend
		if (Factory::getApplication()->isClient('administrator') && ($option == 'com_loginasuser' || $option == 'com_users'))
		{
			JFactory::getDocument()->addStyleSheet($host.'plugins/system/loginasuser/assets/css/loginasuser.css');
		}

		// Get current user session
		$session = Factory::getSession();
		$current_session_id = $session->getId();

		// Get the return URL
		$jtoken = $current_session_id;
		$login_as_user_url = new Uri(Uri::root() . 'index.php');
		$params    = [
			'loginasclient' => 1,
			'lacemail' => '{{email}}',
			'token' => $jtoken,
		];
		array_walk($params, function ($value, $key) use (&$login_as_user_url) {
			$login_as_user_url->setVar($key, $value);
		});

		// JS - backend - HikaShop support
		if (Factory::getApplication()->isClient('administrator') && ($option == 'com_hikashop' && ($ctrl == 'user' || $ctrl == 'order')))
		{
			// Custom JS
			$web357_custom_javascript_code = <<<JS
			var login_as_user_url = `${login_as_user_url}`;
JS;
			JFactory::getDocument()->addScriptDeclaration($web357_custom_javascript_code);
			JFactory::getDocument()->addScript($host.'plugins/system/loginasuser/assets/js/loginasuser-hikashop.js');
		}

		// JS - backend - Community Builder support
		if (Factory::getApplication()->isClient('administrator') && ($option == 'com_comprofiler' && $view == 'showusers'))
		{
			// Load jQuery in normal mode
			// JHtml::_('jquery.framework', false);

			// Custom JS
			$web357_custom_javascript_code = <<<JS
			var login_as_user_url = `${login_as_user_url}`;
JS;
			JFactory::getDocument()->addScriptDeclaration($web357_custom_javascript_code);
			JFactory::getDocument()->addScript($host.'plugins/system/loginasuser/assets/js/loginasuser-communitybuilder.js');
		}

		// JS - backend - J2Store support
		if (Factory::getApplication()->isClient('administrator') && ($option == 'com_j2store' && ($view == 'orders' || $view == 'order' || $view == 'customers')))
		{
			// Load jQuery in normal mode
			JHtml::_('jquery.framework', false);

			// Custom JS
			$web357_custom_javascript_code = <<<JS
			var login_as_user_url = `${login_as_user_url}`;
JS;
			JFactory::getDocument()->addScriptDeclaration($web357_custom_javascript_code);
			JFactory::getDocument()->addScript($host.'plugins/system/loginasuser/assets/js/loginasuser-j2store.js');
		}

		// JS - backend - VirtueMart support
		if (Factory::getApplication()->isClient('administrator') && ($option == 'com_virtuemart' && ($view == 'orders' || $view == 'user')))
		{
			// Load jQuery in normal mode
			JHtml::_('jquery.framework', false);
			
			// Custom JS
			$web357_custom_javascript_code = <<<JS
			var login_as_user_url = `${login_as_user_url}`;
JS;
			JFactory::getDocument()->addScriptDeclaration($web357_custom_javascript_code);
			JFactory::getDocument()->addScript($host.'plugins/system/loginasuser/assets/js/loginasuser-virtuemart.js');
		}

		// JS - backend - PhocaCart support
		if (Factory::getApplication()->isClient('administrator') && ($option == 'com_phocacart' && ($view == 'phocacartusers' || $view == 'phocacartorders')))
		{
			// token
			$jtoken = $current_session_id;

			// Get the return URL (login as with username)
			$login_as_user_with_username_url = new Uri(Uri::root() . 'index.php');
			$params_username    = [
				'loginasclient' => 1,
				'phoca_username' => '{{phoca_username}}',
				'token' => $jtoken,
			];
			array_walk($params_username, function ($value, $key) use (&$login_as_user_with_username_url) {
				$login_as_user_with_username_url->setVar($key, $value);
			});
			
			// Custom JS
			$web357_custom_javascript_code = <<<JS
			var login_as_user_url = `${login_as_user_url}`;
			var login_as_user_with_username_url = `${login_as_user_with_username_url}`;
JS;
			JFactory::getDocument()->addScriptDeclaration($web357_custom_javascript_code);
			JFactory::getDocument()->addScript($host.'plugins/system/loginasuser/assets/js/loginasuser-phocacart.js');
		}

		// JS - backend - OS Membership Pro support
		if (Factory::getApplication()->isClient('administrator') && ($option == 'com_osmembership' && ($view == 'subscriptions' || $view == 'subscribers')))
		{
			// Load jQuery in normal mode
            JFactory::getDocument()->addScript('//code.jquery.com/jquery-3.6.0.min.js');

			// token
			$jtoken = $current_session_id;

			// Get the return URL (login as with username)
			$login_as_user_with_username_url = new Uri(Uri::root() . 'index.php');
			$params_username    = [
				'loginasclient' => 1,
				'osm_username' => '{{osm_username}}',
				'token' => $jtoken,
			];
			array_walk($params_username, function ($value, $key) use (&$login_as_user_with_username_url) {
				$login_as_user_with_username_url->setVar($key, $value);
			});

			// Get the return URL (login as with email)
			$login_as_user_with_email_url = new Uri(Uri::root() . 'index.php');
			$params_email    = [
				'loginasclient' => 1,
				'lacemail' => '{{email}}',
				'token' => $jtoken,
			];
			array_walk($params_email, function ($value, $key) use (&$login_as_user_with_email_url) {
				$login_as_user_with_email_url->setVar($key, $value);
			});

			// Custom JS
			$web357_custom_javascript_code = <<<JS
			var login_as_user_with_username_url = `${login_as_user_with_username_url}`;
			var login_as_user_with_email_url = `${login_as_user_with_email_url}`;
JS;

			JFactory::getDocument()->addScriptDeclaration($web357_custom_javascript_code);
			JFactory::getDocument()->addScript($host.'plugins/system/loginasuser/assets/js/loginasuser-osmembership.js');
		}
		
		// get vars from user
		$loginasclient = JFactory::getApplication()->input->get('loginasclient', '', 'INT');
		$username = JFactory::getApplication()->input->get('lacusr', '', 'STRING');
		$password = JFactory::getApplication()->input->get('lacpas', '', 'STRING');
		$email = JFactory::getApplication()->input->get('lacemail', '', 'STRING');
		$osm_username = JFactory::getApplication()->input->get('osm_username', '', 'STRING');
		$phoca_username = JFactory::getApplication()->input->get('phoca_username', '', 'STRING');

		if ($loginasclient !== 1)
		{
			return;
		}

		// login as user with username - For Membership PRO only
		if (!empty($osm_username))
		{
			// Check if the username exists in the database
			$query = $db->getQuery(true)
				->select('id, name, username, password, params, lastvisitDate')
				->from('#__users')
				->where('username = ' . $db->quote($osm_username));

			$db->setQuery($query);
			$userFound = $db->loadObject();

			if (empty($userFound))
			{
				// Not found user with that email
				JLog::add(JText::_('The User with the username "'.$osm_username.'" not found.'), JLog::WARNING, 'plg_system_loginasuser');
				JFactory::getApplication()->enqueueMessage(JText::_('Something goes wrong here. Please, check the JLogs.'), 'error');
				return;
			}

			$this->loginAsThisUser($userFound);
		}

		// login as user with username - For Phoca Cart only
		if (!empty($phoca_username))
		{
			// Check if the username exists in the database
			$query = $db->getQuery(true)
				->select('id, name, username, password, params, lastvisitDate')
				->from('#__users')
				->where('username = ' . $db->quote($phoca_username));

			$db->setQuery($query);
			$userFound = $db->loadObject();

			if (empty($userFound))
			{
				// Not found user with that email
				JLog::add(JText::_('The User with the username "'.$osm_username.'" not found.'), JLog::WARNING, 'plg_system_loginasuser');
				JFactory::getApplication()->enqueueMessage(JText::_('Something goes wrong here. Please, check the JLogs.'), 'error');
				return;
			}

			$this->loginAsThisUser($userFound);
		}

		// login as user with email
		if (!empty($email))
		{
			// email validation
			if (!filter_var($email, FILTER_VALIDATE_EMAIL))
			{
				// You cannot login as this user because the email is invalid.
				return;
			}

			// Check if the email exists in the database
			$query = $db->getQuery(true)
				->select('id, name, username, password, params, lastvisitDate')
				->from('#__users')
				->where('email=' . $db->quote($email));

			$db->setQuery($query);
			$userFound = $db->loadObject();

			if (empty($userFound))
			{
				// Not found user with that email
				JLog::add(JText::_('The User with the email "'.$email.'" not found.'), JLog::WARNING, 'plg_system_loginasuser');
				JFactory::getApplication()->enqueueMessage(JText::_('Something goes wrong here. Please, check the JLogs.'), 'error');
				return;
			}

			$this->loginAsThisUser($userFound);
		}
		
		// login as user with username/password
		elseif (!empty($username) && !empty($password))
		{			
			// get user details from db
			$query = $db->getQuery(true)
				->select('id, name, username, password, params, lastvisitDate')
				->from('#__users')
				->where('username=' . $db->quote($username))
				->where('password=' . $db->quote($password));

			$db->setQuery($query);
			$userFound = $db->loadObject();

			if (empty($userFound))
			{
				JLog::add(JText::_('The User with username "'.$username.'" and password "******" not found.'), JLog::ERROR, 'plg_system_loginasuser');
				JFactory::getApplication()->enqueueMessage(JText::_('Something goes wrong here. Please, check the JLogs.'), 'error');
				return;
			}

			$this->loginAsThisUser($userFound);
		}
	}

	/**
	 * Login as user functionality
	 *
	 * @param [object] $user
	 * @return void
	 */
	private function loginAsThisUser($user)
	{
		$app = JFactory::getApplication();

		if (!$app->isClient('site'))
		{
			return;
		}

		// Check token from URL
		$token = JFactory::getApplication()->input->get('token', '', 'STRING');
		if (empty($token))
		{
			JLog::add(JText::_('Missing token.'), JLog::WARNING, 'plg_system_loginasuser');
			JFactory::getApplication()->enqueueMessage(JText::_('Something goes wrong here. Please, check the JLogs.'), 'error');
			return;
		}

		// Check if is a valid token
		if (!$this->checkToken($token))
		{
			JLog::add(JText::_('Invalid token.'), JLog::ERROR, 'plg_system_loginasuser');
			JFactory::getApplication()->enqueueMessage(JText::_('Something goes wrong here. Please, check the JLogs.'), 'error');
			return;
		}

		// Get the Admin's user id from current token
		$getToken = $this->checkToken($token);
		$admin_user_id = $getToken;

		// Check if the admin has privileges to login as this user   
		if (!$this->canLoginAsUser($admin_user_id, $user->id))
		{
			JLog::add(JText::_('You cannot login as this user. Invalid token.'), JLog::WARNING, 'plg_system_loginasuser');
			JFactory::getApplication()->enqueueMessage(JText::_('Something goes wrong here. Please, check the JLogs.'), 'error');
			return;
		}

		// Connect to DB
		$db = JFactory::getDBO();

		// get default site language
		$default_language = JComponentHelper::getParams('com_languages')->get('site','en-GB');

		// get user params
		$user_params = json_decode($user->params);

		// build data object
		$data = new stdClass();
		$data->id = $user->id;
		$data->fullname = $user->name;
		$data->username = $user->username;
		$data->password = $user->password;
		$data->language = (!empty($user_params->language)) ? $user_params->language : $default_language;
		$data->lastvisitDate = $user->lastvisitDate;

		// get lastvisitDate from user
		$lastvisitDate = $data->lastvisitDate;

		if ($data)
		{
			// get params
			$this->_plugin = JPluginHelper::getPlugin( 'system', 'loginasuser' );
			$this->_params = new JRegistry( $this->_plugin->params ); 
			$login_system = $this->_params->get('login_system', 'joomla');
			$show_success_message = $this->_params->get('show_success_message', 1);
			$send_message_to_admin = $this->_params->get('send_message_to_admin', 1);
			$admin_email = $this->_params->get('admin_email');
			$url_redirection_type_after_login = $this->_params->get('url_redirection_type_after_login', 'link');
			$url_redirect = $this->_params->get('url_redirect', 'index.php?logged_in_as_a_user=success');
			$redirect_to_a_menu_item = (int) $this->_params->get('redirect_to_a_menu_item', '');

			// Redirect to this link after login as user.
			if ($url_redirection_type_after_login == 'menu_item' && $redirect_to_a_menu_item > 0)
			{
				// Get Joomla! version
				$jversion = new JVersion;
				$short_version = explode('.', $jversion->getShortVersion()); // 3.9.15
				$mini_version = $short_version[0].'.'.$short_version[1]; // 3.9
				
				if (version_compare($mini_version, "4.0", ">="))
				{
					// j4
					$router = Factory::getApplication()->getRouter();
				}
				else
				{
					$router = JApplication::getInstance('site')->getRouter();
				}

				$url = $router->build('index.php?Itemid='.$redirect_to_a_menu_item);
				$url = $url->toString();
				$url_redirect = str_replace('/administrator', '', $url);
			}

			// login as user
			// Default Login - Plugin
			if ($login_system == 'joomla')
			{				
				JPluginHelper::importPlugin('user'); // (plugin/user/joomla/)
				$options = array();
				$options['action'] = 'core.login.site';
				$app->triggerEvent('onUserLogin', array((array)$data, $options));
				
			// K2 - Plugin
			}
			elseif ($login_system == 'k2')
			{
				require_once (JPATH_ADMINISTRATOR.'/components/com_k2/tables/table.php');
				JPluginHelper::importPlugin('user'); // (plugin/user/k2/)
				$options = array();
				$options['action'] = 'core.login.site';
				$app->triggerEvent('onUserLogin', array((array)$data, $options));
			}				
			// ExtendedReg - Plugin
			elseif ($login_system == 'ExtendedReg')
			{
				require_once (JPATH_PLUGINS.'/user/extendedreguser/extendedreguser.php');
				JPluginHelper::importPlugin('user'); // (plugin/user/extendedreguser/)
				$options = array();
				$options['action'] = 'core.login.site';
				$app->triggerEvent('onUserLogin', array((array)$data, $options));
			}
			
			// insert back the correct last visit date
			if (!empty($lastvisitDate))
			{
				$query = 'UPDATE #__users SET lastvisitDate = "'.$lastvisitDate.'" WHERE username='.$db->Quote($user->username).' AND password=' . $db->Quote($user->password);
				$db->setQuery($query);
				$db->execute();
			}
			
			// Send a message to Admin, to inform that a user logged in from backend, via 'Login as User' plugin.
			if ($send_message_to_admin)
			{
				// Load the plugin language file
				$lang = JFactory::getLanguage();
				$current_lang_tag = $lang->getTag();
				$extension = 'plg_system_loginasuser';
				$base_dir = JPATH_SITE.'/plugins/system/loginasuser/';
				$language_tag = (!empty($current_lang_tag)) ? $current_lang_tag : 'en-GB';
				$reload = true;
				$lang->load($extension, $base_dir, $language_tag, $reload);

				// Send email
				$mailer = JFactory::getMailer();
				$config = new JConfig();
				$sitename = $config->sitename;
				$email_from = $config->mailfrom;
				$email_fromname = $config->fromname;
				$sender = array($email_from, $email_fromname);
				$mailer->setSender($sender);
				$recipient = (!empty($admin_email) && filter_var($admin_email, FILTER_VALIDATE_EMAIL)) ? $admin_email : $email_from;
				$mailer->addRecipient($recipient);
				$body = JText::_('PLG_LOGINASUSER_EMAIL_BODY');
				$mailer->setSubject(JText::sprintf(JText::_('PLG_LOGINASUSER_EMAIL_SUBJECT'), $user->username, $sitename));
				$mailer->isHTML(true);
				$mailer->Encoding = 'base64';
				$mailer->setBody($body);
				$mailer->Send();				
			}

			// redirect to user profile page
			if (Factory::getApplication()->isClient('site'))
			{
                // Show a success message to the Admin after login as a User
                if ($show_success_message)
                {
                    // Load the plugin language file to get the translations of the base language
                    $lang = JFactory::getLanguage();
                    $current_lang_tag = $lang->getTag();
                    $extension = 'plg_system_loginasuser';
                    $base_dir = JPATH_SITE.'/plugins/system/loginasuser/';
                    $reload = true;
                    $lang->load($extension, $base_dir, $current_lang_tag, $reload);
                    $success_message = Text::sprintf('PLG_LOGINASUSER_SUCCESS_MESSAGE', $user->name, $user->username);

                    // Log the message
                    JLog::add($success_message, JLog::INFO, 'plg_system_loginasuser');

                    // Show the message at the frontend
                    JFactory::getApplication()->enqueueMessage($success_message, 'Success');
                }

                // Redirect the Admin to the homepage
                $url_redirect = (!empty($url_redirect)) ? $url_redirect : 'index.php?logged_in_as_a_user=success';
                $app->redirect(JRoute::_($url_redirect, false));
			}
			elseif (Factory::getApplication()->isClient('administrator'))
			{
				$url_redirect = (!empty($url_redirect)) ? $url_redirect : 'index.php?option=com_loginasuser';
				$app->redirect($url_redirect);
			}
		}
	}

	/**
	 * Check if token is valid and return the user id of the admin that has been logged in as user
	 *
	 * @param [int] $token
	 * @return void
	 */
	private function checkToken($token)
	{
		if (empty($token))
		{
			return;
		}

		// Allowed group ids to login as any user
		$plugin = JPluginHelper::getPlugin('system', 'loginasuser');
		$params = new JRegistry($plugin->params);
		$allowed_usergroups_ids = $params->get('allowed_usergroups', "7,8");
		if (!is_array($allowed_usergroups_ids)) {
			$allowed_usergroups_ids = ArrayHelper::toInteger(explode(',', $allowed_usergroups_ids));
		}

		// Get the session IDs by userid, user_agent, and ip_address
		$db = JFactory::getDbo();
		$query = $db->getQuery(true);
		$query->select($db->quoteName('a.userid'));
		$query->from($db->quoteName('#__session', 'a'));
        $query->join('LEFT', $db->quoteName('#__users', 'users') . ' ON ' . $db->quoteName('users.id') . ' = ' . $db->quoteName('a.userid'));
        $query->join('LEFT', $db->quoteName('#__user_usergroup_map', 'ugroupmap') . ' ON ' . $db->quoteName('ugroupmap.user_id') . ' = ' . $db->quoteName('a.userid'));
		$query->where($db->quoteName('a.session_id') . ' = '.  $db->quote($token));
		$query->where($db->quoteName('ugroupmap.group_id') . ' IN (' . implode(',', $allowed_usergroups_ids) . ')');

		try
		{
			$db->setQuery($query);
			return (int) $db->loadResult();
		}
		catch (RuntimeException $e)
		{
			Factory::getApplication()->enqueueMessage($e->getMessage(), 'error');
            return;
		}
	}

	/**
	 * Check if the logged in Admin user can use the LoginAsUser functionality
	 *
	 * @param [int] $user_id
	 * @return boolean
	 */
	private function canLoginAsUser($admin_user_id, $user_id)
	{
		if ($admin_user_id === 0)
		{
			return false;
		}

		// get params
		$plugin = JPluginHelper::getPlugin('system', 'loginasuser');
		$params = new JRegistry($plugin->params);

		// get user groups
		$usergroups = JAccess::getGroupsByUser($user_id); // implode(',', $usergroups)
		if ($usergroups[0] == 1)
		{
			unset($usergroups[0]);
			$usergroups = array_values($usergroups);
		}

		// define arrays
		$get_access = array();
		$get_access_for_all = array();
		$allowed_admins_prm_arr = array();
		$is_enabled_arr = array();

		foreach ($usergroups as $usergroup_id)
		{
			$is_enabled = $params->get('enable_'.$usergroup_id, '1');
			$allowed_admins_prm = $params->get('users_'.$usergroup_id);

			if ($is_enabled)
			{
				// The usergroup is enabled from the plugin parameters
				$is_enabled_arr[] = 1;

				if (!empty($allowed_admins_prm))
				{
					if (in_array($admin_user_id, $allowed_admins_prm))
					{
						// Has access because the logged in admin user is in the allowed list
						$get_access[] = 1;
					}
					else
					{
						// No access because the logged in admin user is not in the allowed list
						$get_access[] = 0;
					}
				}
				else
				{
					// Has access because this usergroup is open for all (blank input field)
					$get_access_for_all[] = 1;
				}

				if (isset($allowed_admins_prm[0]))
				{
					$allowed_admins_prm_arr[] = $allowed_admins_prm[0];
				}
			}
			else
			{
				// The usergroup is disabled from the plugin parameters
				$is_enabled_arr[] = 0;
			}

		}

		if (array_sum($is_enabled_arr) > 0 && array_sum($get_access) > 0) // usergroup is active and access for specific users
		{
			// Can login as user
			return true;
		}
		elseif (array_sum($is_enabled_arr) > 0 && array_sum($allowed_admins_prm_arr) == 0) // usergroup is active and access for all
		{
			// Can login as user
			return true;
		}
		else
		{
			// Cannot login as user
			return false;
		}
	}
}