Spade

Mini Shell

Directory:~$ /home/lmsyaran/www/khsh/
Upload File

[Home] [System Details] [Kill Me]
Current File:~$ /home/lmsyaran/www/khsh/system.zip

PKu�[��]��rating_star.pngnu�[����PNG


IHDRVu\��IDATxڕ�=@@`g�K��!����8�^4:��p�d^�$"o�H���LCz�F�D:�
R�g�e6�H�PC	;+9S�	h�A����M�Bq�\���0(�~���l�
Dl�̒!t�^2��P�C���2:;�Uư���UIEND�B`�PKu�[W9����rating_star_blank.pngnu�[����PNG


IHDR�|�l�IDATxc@Y"�%��>�}؄�\�!B��%�����e��
�d�E,\�]�P PD�+��3\�K&�
�p�\T��%�
��|uy���o�,6����V0K��AHh93��Ό.Z
�n><�9��IEND�B`�PKu�[��V\��sort_asc.pngnu�[����PNG


IHDR�|�lGIDATx��G@!��^�Wp���';�	�7�K��������U��6=ҢŎ4k�$�U��OT��A�x���H���IEND�B`�PKu�[J!G`��
sort_desc.pngnu�[����PNG


IHDR�|�lNIDATx��1�`^��4	/�q�+�jo�!2�o|�u���\\�b�8�ڭ���ݙ��ZL`��0E�1��i���T�IEND�B`�PK|��[���/�/actionlogs/actionlogs.phpnu�[���<?php
/**
 * @package     Joomla.Plugins
 * @subpackage  System.actionlogs
 *
 * @copyright   (C) 2018 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\CMS\Cache\Cache;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Form;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\User\User;

/**
 * Joomla! Users Actions Logging Plugin.
 *
 * @since  3.9.0
 */
class PlgSystemActionLogs extends JPlugin
{
	/**
	 * Application object.
	 *
	 * @var    JApplicationCms
	 * @since  3.9.0
	 */
	protected $app;

	/**
	 * Database object.
	 *
	 * @var    JDatabaseDriver
	 * @since  3.9.0
	 */
	protected $db;

	/**
	 * Load plugin language file automatically so that it can be used inside
component
	 *
	 * @var    boolean
	 * @since  3.9.0
	 */
	protected $autoloadLanguage = true;

	/**
	 * Constructor.
	 *
	 * @param   object  &$subject  The object to observe.
	 * @param   array   $config    An optional associative array of
configuration settings.
	 *
	 * @since   3.9.0
	 */
	public function __construct(&$subject, $config)
	{
		parent::__construct($subject, $config);

		// Import actionlog plugin group so that these plugins will be triggered
for events
		PluginHelper::importPlugin('actionlog');
	}

	/**
	 * Adds additional fields to the user editing form for logs e-mail
notifications
	 *
	 * @param   JForm  $form  The form to be altered.
	 * @param   mixed  $data  The associated data for the form.
	 *
	 * @return  boolean
	 *
	 * @since   3.9.0
	 */
	public function onContentPrepareForm($form, $data)
	{
		if (!$form instanceof Form)
		{
			$this->subject->setError('JERROR_NOT_A_FORM');

			return false;
		}

		$formName = $form->getName();

		$allowedFormNames = array(
			'com_users.profile',
			'com_admin.profile',
			'com_users.user',
		);

		if (!in_array($formName, $allowedFormNames))
		{
			return true;
		}

		/**
		 * We only allow users who has Super User permission change this setting
for himself or for other users
		 * who has same Super User permission
		 */

		$user = Factory::getUser();

		if (!$user->authorise('core.admin'))
		{
			return true;
		}

		// If we are on the save command, no data is passed to $data variable, we
need to get it directly from request
		$jformData = $this->app->input->get('jform', array(),
'array');

		if ($jformData && !$data)
		{
			$data = $jformData;
		}

		if (is_array($data))
		{
			$data = (object) $data;
		}

		if (empty($data->id) ||
!User::getInstance($data->id)->authorise('core.admin'))
		{
			return true;
		}

		Form::addFormPath(__DIR__ . '/forms');

		if ((!PluginHelper::isEnabled('actionlog', 'joomla'))
&&
(Factory::getApplication()->isClient('administrator')))
		{
			$form->loadFile('information', false);

			return true;
		}

		if (!PluginHelper::isEnabled('actionlog', 'joomla'))
		{
			return true;
		}

		$form->loadFile('actionlogs', false);
	}

	/**
	 * Runs on content preparation
	 *
	 * @param   string  $context  The context for the data
	 * @param   object  $data     An object containing the data for the form.
	 *
	 * @return  boolean
	 *
	 * @since   3.9.0
	 */
	public function onContentPrepareData($context, $data)
	{
		if (!in_array($context, array('com_users.profile',
'com_admin.profile', 'com_users.user')))
		{
			return true;
		}

		if (is_array($data))
		{
			$data = (object) $data;
		}

		if
(!User::getInstance($data->id)->authorise('core.admin'))
		{
			return true;
		}

		$query = $this->db->getQuery(true)
			->select($this->db->quoteName(array('notify',
'extensions')))
			->from($this->db->quoteName('#__action_logs_users'))
			->where($this->db->quoteName('user_id') . ' =
' . (int) $data->id);

		try
		{
			$values = $this->db->setQuery($query)->loadObject();
		}
		catch (JDatabaseExceptionExecuting $e)
		{
			return false;
		}

		if (!$values)
		{
			return true;
		}

		$data->actionlogs                       = new StdClass;
		$data->actionlogs->actionlogsNotify     = $values->notify;
		$data->actionlogs->actionlogsExtensions = $values->extensions;

		if (!HTMLHelper::isRegistered('users.actionlogsNotify'))
		{
			HTMLHelper::register('users.actionlogsNotify',
array(__CLASS__, 'renderActionlogsNotify'));
		}

		if (!HTMLHelper::isRegistered('users.actionlogsExtensions'))
		{
			HTMLHelper::register('users.actionlogsExtensions',
array(__CLASS__, 'renderActionlogsExtensions'));
		}

		return true;
	}

	/**
	 * Runs after the HTTP response has been sent to the client and delete log
records older than certain days
	 *
	 * @return  void
	 *
	 * @since   3.9.0
	 */
	public function onAfterRespond()
	{
		$daysToDeleteAfter = (int)
$this->params->get('logDeletePeriod', 0);

		if ($daysToDeleteAfter <= 0)
		{
			return;
		}

		// The delete frequency will be once per day
		$deleteFrequency = 3600 * 24;

		// Do we need to run? Compare the last run timestamp stored in the
plugin's options with the current
		// timestamp. If the difference is greater than the cache timeout we
shall not execute again.
		$now  = time();
		$last = (int) $this->params->get('lastrun', 0);

		if (abs($now - $last) < $deleteFrequency)
		{
			return;
		}

		// Update last run status
		$this->params->set('lastrun', $now);

		$db    = $this->db;
		$query = $db->getQuery(true)
			->update($db->qn('#__extensions'))
			->set($db->qn('params') . ' = ' .
$db->q($this->params->toString('JSON')))
			->where($db->qn('type') . ' = ' .
$db->q('plugin'))
			->where($db->qn('folder') . ' = ' .
$db->q('system'))
			->where($db->qn('element') . ' = ' .
$db->q('actionlogs'));

		try
		{
			// Lock the tables to prevent multiple plugin executions causing a race
condition
			$db->lockTable('#__extensions');
		}
		catch (Exception $e)
		{
			// If we can't lock the tables it's too risky to continue
execution
			return;
		}

		try
		{
			// Update the plugin parameters
			$result = $db->setQuery($query)->execute();

			$this->clearCacheGroups(array('com_plugins'), array(0, 1));
		}
		catch (Exception $exc)
		{
			// If we failed to execute
			$db->unlockTables();
			$result = false;
		}

		try
		{
			// Unlock the tables after writing
			$db->unlockTables();
		}
		catch (Exception $e)
		{
			// If we can't lock the tables assume we have somehow failed
			$result = false;
		}

		// Abort on failure
		if (!$result)
		{
			return;
		}

		$daysToDeleteAfter = (int)
$this->params->get('logDeletePeriod', 0);
		$now = $db->quote(Factory::getDate()->toSql());

		if ($daysToDeleteAfter > 0)
		{
			$conditions = array($db->quoteName('log_date') . '
< ' . $query->dateAdd($now, -1 * $daysToDeleteAfter, '
DAY'));

			$query->clear()
				->delete($db->quoteName('#__action_logs'))->where($conditions);
			$db->setQuery($query);

			try
			{
				$db->execute();
			}
			catch (RuntimeException $e)
			{
				// Ignore it
				return;
			}
		}
	}

	/**
	 * Utility method to act on a user after it has been saved.
	 *
	 * @param   array    $user     Holds the new user data.
	 * @param   boolean  $isNew    True if a new user is stored.
	 * @param   boolean  $success  True if user was successfully stored in the
database.
	 * @param   string   $msg      Message.
	 *
	 * @return  boolean
	 *
	 * @since   3.9.0
	 */
	public function onUserAfterSave($user, $isNew, $success, $msg)
	{
		if (!$success)
		{
			return false;
		}

		// Clear access rights in case user groups were changed.
		$userObject = new User($user['id']);
		$userObject->clearAccessRights();
		$authorised = $userObject->authorise('core.admin');

		$query = $this->db->getQuery(true)
			->select('COUNT(*)')
			->from($this->db->quoteName('#__action_logs_users'))
			->where($this->db->quoteName('user_id') . ' =
' . (int) $user['id']);

		try
		{
			$exists = (bool) $this->db->setQuery($query)->loadResult();
		}
		catch (JDatabaseExceptionExecuting $e)
		{
			return false;
		}

		// If preferences don't exist, insert.
		if (!$exists && $authorised &&
isset($user['actionlogs']))
		{
			$values  = array((int) $user['id'], (int)
$user['actionlogs']['actionlogsNotify']);
			$columns = array('user_id', 'notify');

			if
(isset($user['actionlogs']['actionlogsExtensions']))
			{
				$values[]  =
$this->db->quote(json_encode($user['actionlogs']['actionlogsExtensions']));
				$columns[] = 'extensions';
			}

			$query = $this->db->getQuery(true)
				->insert($this->db->quoteName('#__action_logs_users'))
				->columns($this->db->quoteName($columns))
				->values(implode(',', $values));
		}
		elseif ($exists && $authorised &&
isset($user['actionlogs']))
		{
			// Update preferences.
			$values = array($this->db->quoteName('notify') . '
= ' . (int)
$user['actionlogs']['actionlogsNotify']);

			if
(isset($user['actionlogs']['actionlogsExtensions']))
			{
				$values[] = $this->db->quoteName('extensions') . '
= ' .
$this->db->quote(json_encode($user['actionlogs']['actionlogsExtensions']));
			}

			$query = $this->db->getQuery(true)
				->update($this->db->quoteName('#__action_logs_users'))
				->set($values)
				->where($this->db->quoteName('user_id') . ' =
' . (int) $user['id']);
		}
		elseif ($exists && !$authorised)
		{
			// Remove preferences if user is not authorised.
			$query = $this->db->getQuery(true)
				->delete($this->db->quoteName('#__action_logs_users'))
				->where($this->db->quoteName('user_id') . ' =
' . (int) $user['id']);
		}

		try
		{
			$this->db->setQuery($query)->execute();
		}
		catch (JDatabaseExceptionExecuting $e)
		{
			return false;
		}

		return true;
	}

	/**
	 * Removes user preferences
	 *
	 * Method is called after user data is deleted from the database
	 *
	 * @param   array    $user     Holds the user data
	 * @param   boolean  $success  True if user was successfully stored in the
database
	 * @param   string   $msg      Message
	 *
	 * @return  boolean
	 *
	 * @since   3.9.0
	 */
	public function onUserAfterDelete($user, $success, $msg)
	{
		if (!$success)
		{
			return false;
		}

		$query = $this->db->getQuery(true)
			->delete($this->db->quoteName('#__action_logs_users'))
			->where($this->db->quoteName('user_id') . ' =
' . (int) $user['id']);

		try
		{
			$this->db->setQuery($query)->execute();
		}
		catch (JDatabaseExceptionExecuting $e)
		{
			return false;
		}

		return true;
	}

	/**
	 * Clears cache groups. We use it to clear the plugins cache after we
update the last run timestamp.
	 *
	 * @param   array  $clearGroups   The cache groups to clean
	 * @param   array  $cacheClients  The cache clients (site, admin) to clean
	 *
	 * @return  void
	 *
	 * @since   3.9.0
	 */
	private function clearCacheGroups(array $clearGroups, array $cacheClients
= array(0, 1))
	{
		$conf = Factory::getConfig();

		foreach ($clearGroups as $group)
		{
			foreach ($cacheClients as $clientId)
			{
				try
				{
					$options = array(
						'defaultgroup' => $group,
						'cachebase'    => $clientId ? JPATH_ADMINISTRATOR .
'/cache' :
							$conf->get('cache_path', JPATH_SITE .
'/cache')
					);

					$cache = Cache::getInstance('callback', $options);
					$cache->clean();
				}
				catch (Exception $e)
				{
					// Ignore it
				}
			}
		}
	}

	/**
	 * Method to render a value.
	 *
	 * @param   integer|string  $value  The value (0 or 1).
	 *
	 * @return  string  The rendered value.
	 *
	 * @since   3.9.16
	 */
	public static function renderActionlogsNotify($value)
	{
		return Text::_($value ? 'JYES' : 'JNO');
	}

	/**
	 * Method to render a list of extensions.
	 *
	 * @param   array|string  $extensions  Array of extensions or an empty
string if none selected.
	 *
	 * @return  string  The rendered value.
	 *
	 * @since   3.9.16
	 */
	public static function renderActionlogsExtensions($extensions)
	{
		// No extensions selected.
		if (!$extensions)
		{
			return Text::_('JNONE');
		}

		// Load the helper.
		JLoader::register('ActionlogsHelper', JPATH_ADMINISTRATOR .
'/components/com_actionlogs/helpers/actionlogs.php');

		foreach ($extensions as &$extension)
		{
			// Load extension language files and translate extension name.
			ActionlogsHelper::loadTranslationFiles($extension);
			$extension = Text::_($extension);
		}

		return implode(', ', $extensions);
	}
}
PK|��[؞���actionlogs/actionlogs.xmlnu�[���<?xml
version="1.0" encoding="UTF-8"?>
<extension version="3.9" type="plugin"
group="system" method="upgrade">
	<name>PLG_SYSTEM_ACTIONLOGS</name>
	<author>Joomla! Project</author>
	<creationDate>May 2018</creationDate>
	<copyright>(C) 2018 Open Source Matters, Inc.</copyright>
	<license>GNU General Public License version 2 or later; see
LICENSE.txt</license>
	<authorEmail>admin@joomla.org</authorEmail>
	<authorUrl>www.joomla.org</authorUrl>
	<version>3.9.0</version>
	<description>PLG_SYSTEM_ACTIONLOGS_XML_DESCRIPTION</description>
	<files>
		<filename
plugin="actionlogs">actionlogs.php</filename>
		<folder>forms</folder>
	</files>
	<languages>
		<language
tag="en-GB">en-GB.plg_system_actionlogs.ini</language>
		<language
tag="en-GB">en-GB.plg_system_actionlogs.sys.ini</language>
	</languages>
	<config>
		<fields name="params">
			<fieldset name="basic">
				<field
					name="logDeletePeriod"
					type="number"
					label="PLG_SYSTEM_ACTIONLOGS_LOG_DELETE_PERIOD"
					description="PLG_SYSTEM_ACTIONLOGS_LOG_DELETE_PERIOD_DESC"
					default="0"
					min="0"
					filter="int"
					validate="number"
				/>
				<field
					name="lastrun"
					type="hidden"
					default="0"
					filter="integer"
				/>
			</fieldset>
		</fields>
	</config>
</extension>
PK|��[��<__actionlogs/forms/actionlogs.xmlnu�[���<?xml
version="1.0" encoding="UTF-8"?>
<form>
	<fieldset name="actionlogs"
label="PLG_SYSTEM_ACTIONLOGS_OPTIONS"
addfieldpath="/administrator/components/com_actionlogs/models/fields">
		<fields name="actionlogs">
			<field
				name="actionlogsNotify"
				type="radio"
				label="PLG_SYSTEM_ACTIONLOGS_NOTIFICATIONS"
				description="PLG_SYSTEM_ACTIONLOGS_NOTIFICATIONS_DESC"
				class="btn-group btn-group-yesno"
				default="0"
				filter="integer"
				required="true"
				>
				<option value="1">JYES</option>
				<option value="0">JNO</option>
			</field>
			<field
				name="actionlogsExtensions"
				type="logtype"
				label="PLG_SYSTEM_ACTIONLOGS_EXTENSIONS_NOTIFICATIONS"
				description="PLG_SYSTEM_ACTIONLOGS_EXTENSIONS_NOTIFICATIONS_DESC"
				multiple="true"
				validate="options"
				showon="actionlogsNotify:1"
			/>
		</fields>
	</fieldset>
</form>
PK|��[�����
actionlogs/forms/information.xmlnu�[���<?xml
version="1.0" encoding="UTF-8"?>
<form>
	<fields name="params">
		<fieldset name="information"
label="PLG_SYSTEM_ACTIONLOGS_OPTIONS"
addfieldpath="/administrator/components/com_actionlogs/models/fields">
			<field
				name="Information"
				type="plugininfo"
				label="PLG_SYSTEM_ACTIONLOGS_INFO_LABEL"
				description="PLG_SYSTEM_ACTIONLOGS_INFO_DESC"
			/>
		</fieldset>
	</fields>
</form>
PK|��[�~�cccache/cache.phpnu�[���<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.cache
 *
 * @copyright   (C) 2007 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Joomla! Page Cache Plugin.
 *
 * @since  1.5
 */
class PlgSystemCache extends JPlugin
{
	/**
	 * Cache instance.
	 *
	 * @var    JCache
	 * @since  1.5
	 */
	public $_cache;

	/**
	 * Cache key
	 *
	 * @var    string
	 * @since  3.0
	 */
	public $_cache_key;

	/**
	 * Application object.
	 *
	 * @var    JApplicationCms
	 * @since  3.8.0
	 */
	protected $app;

	/**
	 * Constructor.
	 *
	 * @param   object  &$subject  The object to observe.
	 * @param   array   $config    An optional associative array of
configuration settings.
	 *
	 * @since   1.5
	 */
	public function __construct(& $subject, $config)
	{
		parent::__construct($subject, $config);

		// Get the application if not done by JPlugin.
		if (!isset($this->app))
		{
			$this->app = JFactory::getApplication();
		}

		// Set the cache options.
		$options = array(
			'defaultgroup' => 'page',
			'browsercache' =>
$this->params->get('browsercache', 0),
			'caching'      => false,
		);

		// Instantiate cache with previous options and create the cache key
identifier.
		$this->_cache     = JCache::getInstance('page', $options);
		$this->_cache_key = JUri::getInstance()->toString();
	}

	/**
	 * Get a cache key for the current page based on the url and possible
other factors.
	 *
	 * @return  string
	 *
	 * @since   3.7
	 */
	protected function getCacheKey()
	{
		static $key;

		if (!$key)
		{
			JPluginHelper::importPlugin('pagecache');

			$parts =
JEventDispatcher::getInstance()->trigger('onPageCacheGetKey');
			$parts[] = JUri::getInstance()->toString();

			$key = md5(serialize($parts));
		}

		return $key;
	}

	/**
	 * After Initialise Event.
	 * Checks if URL exists in cache, if so dumps it directly and closes.
	 *
	 * @return  void
	 *
	 * @since   1.5
	 */
	public function onAfterInitialise()
	{
		if ($this->app->isClient('administrator') ||
$this->app->get('offline', '0') ||
$this->app->getMessageQueue())
		{
			return;
		}

		// If any pagecache plugins return false for onPageCacheSetCaching, do
not use the cache.
		JPluginHelper::importPlugin('pagecache');

		$results =
JEventDispatcher::getInstance()->trigger('onPageCacheSetCaching');
		$caching = !in_array(false, $results, true);

		if ($caching && JFactory::getUser()->guest &&
$this->app->input->getMethod() === 'GET')
		{
			$this->_cache->setCaching(true);
		}

		$data = $this->_cache->get($this->getCacheKey());

		// If page exist in cache, show cached page.
		if ($data !== false)
		{
			// Set HTML page from cache.
			$this->app->setBody($data);

			// Dumps HTML page.
			echo $this->app->toString((bool)
$this->app->get('gzip'));

			// Mark afterCache in debug and run debug onAfterRespond events.
			// e.g., show Joomla Debug Console if debug is active.
			if (JDEBUG)
			{
				JProfiler::getInstance('Application')->mark('afterCache');
				JEventDispatcher::getInstance()->trigger('onAfterRespond');
			}

			// Closes the application.
			$this->app->close();
		}
	}

	/**
	 * After Render Event.
	 * Verify if current page is not excluded from cache.
	 *
	 * @return   void
	 *
	 * @since   3.9.12
	 */
	public function onAfterRender()
	{
		if ($this->_cache->getCaching() === false)
		{
			return;
		}

		// We need to check if user is guest again here, because auto-login
plugins have not been fired before the first aid check.
		// Page is excluded if excluded in plugin settings.
		if (!JFactory::getUser()->guest || $this->app->getMessageQueue()
|| $this->isExcluded() === true)
		{
			$this->_cache->setCaching(false);

			return;
		}

		// Disable compression before caching the page.
		$this->app->set('gzip', false);
	}

	/**
	 * After Respond Event.
	 * Stores page in cache.
	 *
	 * @return   void
	 *
	 * @since   1.5
	 */
	public function onAfterRespond()
	{
		if ($this->_cache->getCaching() === false)
		{
			return;
		}

		// Saves current page in cache.
		$this->_cache->store($this->app->getBody(),
$this->getCacheKey());
	}

	/**
	 * Check if the page is excluded from the cache or not.
	 *
	 * @return   boolean  True if the page is excluded else false
	 *
	 * @since    3.5
	 */
	protected function isExcluded()
	{
		// Check if menu items have been excluded.
		if ($exclusions =
$this->params->get('exclude_menu_items', array()))
		{
			// Get the current menu item.
			$active = $this->app->getMenu()->getActive();

			if ($active && $active->id && in_array((int)
$active->id, (array) $exclusions))
			{
				return true;
			}
		}

		// Check if regular expressions are being used.
		if ($exclusions = $this->params->get('exclude',
''))
		{
			// Normalize line endings.
			$exclusions = str_replace(array("\r\n", "\r"),
"\n", $exclusions);

			// Split them.
			$exclusions = explode("\n", $exclusions);

			// Gets internal URI.
			$internal_uri	= '/index.php?' .
JUri::getInstance()->buildQuery($this->app->getRouter()->getVars());

			// Loop through each pattern.
			if ($exclusions)
			{
				foreach ($exclusions as $exclusion)
				{
					// Make sure the exclusion has some content
					if ($exclusion !== '')
					{
						// Test both external and internal URI
						if (preg_match('#' . $exclusion . '#i',
$this->_cache_key . ' ' . $internal_uri, $match))
						{
							return true;
						}
					}
				}
			}
		}

		// If any pagecache plugins return true for onPageCacheIsExcluded,
exclude.
		JPluginHelper::importPlugin('pagecache');

		$results =
JEventDispatcher::getInstance()->trigger('onPageCacheIsExcluded');

		return in_array(true, $results, true);
	}
}
PK|��[a6�cache/cache.xmlnu�[���<?xml
version="1.0" encoding="utf-8"?>
<extension version="3.1" type="plugin"
group="system" method="upgrade">
	<name>plg_system_cache</name>
	<author>Joomla! Project</author>
	<creationDate>February 2007</creationDate>
	<copyright>(C) 2007 Open Source Matters, Inc.</copyright>
	<license>GNU General Public License version 2 or later; see
LICENSE.txt</license>
	<authorEmail>admin@joomla.org</authorEmail>
	<authorUrl>www.joomla.org</authorUrl>
	<version>3.0.0</version>
	<description>PLG_CACHE_XML_DESCRIPTION</description>
	<files>
		<filename plugin="cache">cache.php</filename>
	</files>
	<languages>
		<language
tag="en-GB">en-GB.plg_system_cache.ini</language>
		<language
tag="en-GB">en-GB.plg_system_cache.sys.ini</language>
	</languages>
	<config>
		<fields name="params">
			<fieldset name="basic">
				<field
					name="browsercache"
					type="radio"
					label="PLG_CACHE_FIELD_BROWSERCACHE_LABEL"
					description="PLG_CACHE_FIELD_BROWSERCACHE_DESC"
					class="btn-group btn-group-yesno"
					default="0"
					filter="integer"
					>
					<option value="1">JYES</option>
					<option value="0">JNO</option>
				</field>

				<field
					name="exclude_menu_items"
					type="menuitem"
					label="PLG_CACHE_FIELD_EXCLUDE_MENU_ITEMS_LABEL"
					description="PLG_CACHE_FIELD_EXCLUDE_MENU_ITEMS_DESC"
					multiple="multiple"
					filter="int_array"
				/>

			</fieldset>
			<fieldset name="advanced">
				<field
					name="exclude"
					type="textarea"
					label="PLG_CACHE_FIELD_EXCLUDE_LABEL"
					description="PLG_CACHE_FIELD_EXCLUDE_DESC"
					class="input-xxlarge"
					rows="15"
					filter="raw"
				/>

			</fieldset>
		</fields>
	</config>
</extension>
PK|��[�+Gb�b�debug/debug.phpnu�[���<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.Debug
 *
 * @copyright   (C) 2006 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\Utilities\ArrayHelper;

/**
 * Joomla! Debug plugin.
 *
 * @since  1.5
 */
class PlgSystemDebug extends JPlugin
{
	/**
	 * xdebug.file_link_format from the php.ini.
	 *
	 * @var    string
	 * @since  1.7
	 */
	protected $linkFormat = '';

	/**
	 * True if debug lang is on.
	 *
	 * @var    boolean
	 * @since  3.0
	 */
	private $debugLang = false;

	/**
	 * Holds log entries handled by the plugin.
	 *
	 * @var    array
	 * @since  3.1
	 */
	private $logEntries = array();

	/**
	 * Holds SHOW PROFILES of queries.
	 *
	 * @var    array
	 * @since  3.1.2
	 */
	private $sqlShowProfiles = array();

	/**
	 * Holds all SHOW PROFILE FOR QUERY n, indexed by n-1.
	 *
	 * @var    array
	 * @since  3.1.2
	 */
	private $sqlShowProfileEach = array();

	/**
	 * Holds all EXPLAIN EXTENDED for all queries.
	 *
	 * @var    array
	 * @since  3.1.2
	 */
	private $explains = array();

	/**
	 * Holds total amount of executed queries.
	 *
	 * @var    int
	 * @since  3.2
	 */
	private $totalQueries = 0;

	/**
	 * Application object.
	 *
	 * @var    JApplicationCms
	 * @since  3.3
	 */
	protected $app;

	/**
	 * Database object.
	 *
	 * @var    JDatabaseDriver
	 * @since  3.8.0
	 */
	protected $db;

	/**
	 * Container for callback functions to be triggered when rendering the
console.
	 *
	 * @var    callable[]
	 * @since  3.7.0
	 */
	private static $displayCallbacks = array();

	/**
	 * Constructor.
	 *
	 * @param   object  &$subject  The object to observe.
	 * @param   array   $config    An optional associative array of
configuration settings.
	 *
	 * @since   1.5
	 */
	public function __construct(&$subject, $config)
	{
		parent::__construct($subject, $config);

		// Log the deprecated API.
		if ($this->params->get('log-deprecated', 0))
		{
			JLog::addLogger(array('text_file' =>
'deprecated.php'), JLog::ALL, array('deprecated'));
		}

		// Log everything (except deprecated APIs, these are logged separately
with the option above).
		if ($this->params->get('log-everything', 0))
		{
			JLog::addLogger(array('text_file' =>
'everything.php'), JLog::ALL, array('deprecated',
'databasequery'), true);
		}

		// Get the application if not done by JPlugin. This may happen during
upgrades from Joomla 2.5.
		if (!$this->app)
		{
			$this->app = JFactory::getApplication();
		}

		// Get the db if not done by JPlugin. This may happen during upgrades
from Joomla 2.5.
		if (!$this->db)
		{
			$this->db = JFactory::getDbo();
		}

		$this->debugLang = $this->app->get('debug_lang');

		// Skip the plugin if debug is off
		if ($this->debugLang == '0' &&
$this->app->get('debug') == '0')
		{
			return;
		}

		// Only if debugging or language debug is enabled.
		if (JDEBUG || $this->debugLang)
		{
			JFactory::getConfig()->set('gzip', 0);
			ob_start();
			ob_implicit_flush(false);
		}

		$this->linkFormat = ini_get('xdebug.file_link_format');

		if ($this->params->get('logs', 1))
		{
			$priority = 0;

			foreach ($this->params->get('log_priorities', array())
as $p)
			{
				$const = 'JLog::' . strtoupper($p);

				if (!defined($const))
				{
					continue;
				}

				$priority |= constant($const);
			}

			// Split into an array at any character other than alphabet, numbers, _,
., or -
			$categories = preg_split('/[^\w.-]+/',
$this->params->get('log_categories', ''), -1,
PREG_SPLIT_NO_EMPTY);
			$mode       = $this->params->get('log_category_mode',
0);

			JLog::addLogger(array('logger' => 'callback',
'callback' => array($this, 'logger')), $priority,
$categories, $mode);
		}

		// Prepare disconnect handler for SQL profiling.
		$db = $this->db;
		$db->addDisconnectHandler(array($this,
'mysqlDisconnectHandler'));

		// Log deprecated class aliases
		foreach (JLoader::getDeprecatedAliases() as $deprecation)
		{
			JLog::add(
				sprintf(
					'%1$s has been aliased to %2$s and the former class name is
deprecated. The alias will be removed in %3$s.',
					$deprecation['old'],
					$deprecation['new'],
					$deprecation['version']
				),
				JLog::WARNING,
				'deprecated'
			);
		}
	}

	/**
	 * Add the CSS for debug.
	 * We can't do this in the constructor because stuff breaks.
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	public function onAfterDispatch()
	{
		// Only if debugging or language debug is enabled.
		if ((JDEBUG || $this->debugLang) &&
$this->isAuthorisedDisplayDebug())
		{
			JHtml::_('stylesheet', 'cms/debug.css',
array('version' => 'auto', 'relative'
=> true));
		}

		// Disable asset media version if needed.
		if (JDEBUG && (int)
$this->params->get('refresh_assets', 1) === 0)
		{
			$this->app->getDocument()->setMediaVersion(null);
		}

		// Only if debugging is enabled for SQL query popovers.
		if (JDEBUG && $this->isAuthorisedDisplayDebug())
		{
			JHtml::_('bootstrap.tooltip');
			JHtml::_('bootstrap.popover', '.hasPopover',
array('placement' => 'top'));
		}
	}

	/**
	 * Show the debug info.
	 *
	 * @return  void
	 *
	 * @since   1.6
	 */
	public function onAfterRespond()
	{
		// Do not render if debugging or language debug is not enabled.
		if (!JDEBUG && !$this->debugLang)
		{
			return;
		}

		// User has to be authorised to see the debug information.
		if (!$this->isAuthorisedDisplayDebug())
		{
			return;
		}

		// Only render for HTML output.
		if (JFactory::getDocument()->getType() !== 'html')
		{
			return;
		}

		// Capture output.
		$contents = ob_get_contents();

		if ($contents)
		{
			ob_end_clean();
		}

		// No debug for Safari and Chrome redirection.
		if (strpos($contents, '<html><head><meta
http-equiv="refresh" content="0;') === 0
			&&
strpos(strtolower(isset($_SERVER['HTTP_USER_AGENT']) ?
$_SERVER['HTTP_USER_AGENT'] : ''), 'webkit')
!== false)
		{
			echo $contents;

			return;
		}

		// Load language.
		$this->loadLanguage();

		$html = array();

		// Some "mousewheel protecting" JS.
		$html[] = "<script>function toggleContainer(name)
		{
			var e = document.getElementById(name);// MooTools might not be available
;)
			e.style.display = e.style.display === 'none' ?
'block' : 'none';
		}</script>";

		$html[] = '<div id="system-debug"
class="profiler">';

		$html[] = '<h2>' . JText::_('PLG_DEBUG_TITLE')
. '</h2>';

		if (JDEBUG)
		{
			if (JError::getErrors())
			{
				$html[] = $this->display('errors');
			}

			if ($this->params->get('session', 1))
			{
				$html[] = $this->display('session');
			}

			if ($this->params->get('profile', 1))
			{
				$html[] = $this->display('profile_information');
			}

			if ($this->params->get('memory', 1))
			{
				$html[] = $this->display('memory_usage');
			}

			if ($this->params->get('queries', 1))
			{
				$html[] = $this->display('queries');
			}

			if (!empty($this->logEntries) &&
$this->params->get('logs', 1))
			{
				$html[] = $this->display('logs');
			}
		}

		if ($this->debugLang)
		{
			if ($this->params->get('language_errorfiles', 1))
			{
				$languageErrors = JFactory::getLanguage()->getErrorFiles();
				$html[]         =
$this->display('language_files_in_error', $languageErrors);
			}

			if ($this->params->get('language_files', 1))
			{
				$html[] = $this->display('language_files_loaded');
			}

			if ($this->params->get('language_strings', 1))
			{
				$html[] = $this->display('untranslated_strings');
			}
		}

		foreach (self::$displayCallbacks as $name => $callable)
		{
			$html[] = $this->displayCallback($name, $callable);
		}

		$html[] = '</div>';

		echo str_replace('</body>', implode('', $html)
. '</body>', $contents);
	}

	/**
	 * Add a display callback to be rendered with the debug console.
	 *
	 * @param   string    $name      The name of the callable, this is used to
generate the section title.
	 * @param   callable  $callable  The callback function to be added.
	 *
	 * @return  boolean
	 *
	 * @since   3.7.0
	 * @throws  InvalidArgumentException
	 */
	public static function addDisplayCallback($name, $callable)
	{
		// TODO - When PHP 5.4 is the minimum the parameter should be typehinted
"callable" and this check removed
		if (!is_callable($callable))
		{
			throw new InvalidArgumentException('A valid callback function must
be given.');
		}

		self::$displayCallbacks[$name] = $callable;

		return true;
	}

	/**
	 * Remove a registered display callback
	 *
	 * @param   string  $name  The name of the callable.
	 *
	 * @return  boolean
	 *
	 * @since   3.7.0
	 */
	public static function removeDisplayCallback($name)
	{
		unset(self::$displayCallbacks[$name]);

		return true;
	}

	/**
	 * Method to check if the current user is allowed to see the debug
information or not.
	 *
	 * @return  boolean  True if access is allowed.
	 *
	 * @since   3.0
	 */
	private function isAuthorisedDisplayDebug()
	{
		static $result = null;

		if ($result !== null)
		{
			return $result;
		}

		// If the user is not allowed to view the output then end here.
		$filterGroups = (array)
$this->params->get('filter_groups', array());

		if (!empty($filterGroups))
		{
			$userGroups = JFactory::getUser()->get('groups');

			if (!array_intersect($filterGroups, $userGroups))
			{
				$result = false;

				return false;
			}
		}

		$result = true;

		return true;
	}

	/**
	 * General display method.
	 *
	 * @param   string  $item    The item to display.
	 * @param   array   $errors  Errors occurred during execution.
	 *
	 * @return  string
	 *
	 * @since   2.5
	 */
	protected function display($item, array $errors = array())
	{
		$title = JText::_('PLG_DEBUG_' . strtoupper($item));

		$status = '';

		if (count($errors))
		{
			$status = ' dbg-error';
		}

		$fncName = 'display' . ucfirst(str_replace('_',
'', $item));

		if (!method_exists($this, $fncName))
		{
			return __METHOD__ . ' -- Unknown method: ' . $fncName .
'<br />';
		}

		$html = array();

		$js = "toggleContainer('dbg_container_" . $item .
"');";

		$class = 'dbg-header' . $status;

		$html[] = '<div class="' . $class . '"
onclick="' . $js . '"><a
href="javascript:void(0);"><h3>' . $title .
'</h3></a></div>';

		// @todo set with js.. ?
		$style = ' style="display: none;"';

		$html[] = '<div ' . $style . '
class="dbg-container" id="dbg_container_' . $item .
'">';
		$html[] = $this->$fncName();
		$html[] = '</div>';

		return implode('', $html);
	}

	/**
	 * Display method for callback functions.
	 *
	 * @param   string    $name      The name of the callable.
	 * @param   callable  $callable  The callable function.
	 *
	 * @return  string
	 *
	 * @since   3.7.0
	 */
	protected function displayCallback($name, $callable)
	{
		$title = JText::_('PLG_DEBUG_' . strtoupper($name));

		$html = array();

		$js = "toggleContainer('dbg_container_" . $name .
"');";

		$class = 'dbg-header';

		$html[] = '<div class="' . $class . '"
onclick="' . $js . '"><a
href="javascript:void(0);"><h3>' . $title .
'</h3></a></div>';

		// @todo set with js.. ?
		$style = ' style="display: none;"';

		$html[] = '<div ' . $style . '
class="dbg-container" id="dbg_container_' . $name .
'">';
		$html[] = call_user_func($callable);
		$html[] = '</div>';

		return implode('', $html);
	}

	/**
	 * Display session information.
	 *
	 * Called recursively.
	 *
	 * @param   string   $key      A session key.
	 * @param   mixed    $session  The session array, initially null.
	 * @param   integer  $id       Used to identify the DIV for the JavaScript
toggling code.
	 *
	 * @return  string
	 *
	 * @since   2.5
	 */
	protected function displaySession($key = '', $session = null,
$id = 0)
	{
		if (!$session)
		{
			$session = JFactory::getSession()->getData();
		}

		$html = array();
		static $id;

		if (!is_array($session))
		{
			$html[] = $key . '<pre>' .
$this->prettyPrintJSON($session) . '</pre>' . PHP_EOL;
		}
		else
		{
			foreach ($session as $sKey => $entries)
			{
				$display = true;

				if (is_array($entries) && $entries)
				{
					$display = false;
				}

				if (is_object($entries))
				{
					$o = ArrayHelper::fromObject($entries);

					if ($o)
					{
						$entries = $o;
						$display = false;
					}
				}

				if (!$display)
				{
					$js = "toggleContainer('dbg_container_session" . $id .
'_' . $sKey . "');";

					$html[] = '<div class="dbg-header"
onclick="' . $js . '"><a
href="javascript:void(0);"><h3>' . $sKey .
'</h3></a></div>';

					// @todo set with js.. ?
					$style = ' style="display: none;"';

					$html[] = '<div ' . $style . '
class="dbg-container" id="dbg_container_session' . $id
. '_' . $sKey . '">';
					$id++;

					// Recurse...
					$this->displaySession($sKey, $entries, $id);

					$html[] = '</div>';

					continue;
				}

				if (is_array($entries))
				{
					$entries = implode($entries);
				}

				if (is_string($entries))
				{
					$html[] = $sKey . '<pre>' .
$this->prettyPrintJSON($entries) . '</pre>' . PHP_EOL;
				}
			}
		}

		return implode('', $html);
	}

	/**
	 * Display errors.
	 *
	 * @return  string
	 *
	 * @since   2.5
	 */
	protected function displayErrors()
	{
		$html = array();

		$html[] = '<ol>';

		while ($error = JError::getError(true))
		{
			$col = (E_WARNING == $error->get('level')) ?
'red' : 'orange';

			$html[] = '<li>';
			$html[] = '<b style="color: ' . $col .
'">' . $error->getMessage() . '</b><br
/>';

			$info = $error->get('info');

			if ($info)
			{
				$html[] = '<pre>' . print_r($info, true) .
'</pre><br />';
			}

			$html[] = $this->renderBacktrace($error);
			$html[] = '</li>';
		}

		$html[] = '</ol>';

		return implode('', $html);
	}

	/**
	 * Display profile information.
	 *
	 * @return  string
	 *
	 * @since   2.5
	 */
	protected function displayProfileInformation()
	{
		$html = array();

		$htmlMarks = array();

		$totalTime = 0;
		$totalMem  = 0;
		$marks     = array();
		$bars      = array();
		$barsMem   = array();

		foreach (JProfiler::getInstance('Application')->getMarks()
as $mark)
		{
			$totalTime += $mark->time;
			$totalMem  += (float) $mark->memory;
			$htmlMark  = sprintf(
				JText::_('PLG_DEBUG_TIME') . ': <span
class="label label-time">%.2f&nbsp;ms</span> /
<span class="label
label-default">%.2f&nbsp;ms</span>'
				. ' ' . JText::_('PLG_DEBUG_MEMORY') . ':
<span class="label label-memory">%0.3f MB</span> /
<span class="label label-default">%0.2f
MB</span>'
				. ' %s: %s',
				$mark->time,
				$mark->totalTime,
				$mark->memory,
				$mark->totalMemory,
				$mark->prefix,
				$mark->label
			);

			$marks[] = (object) array(
				'time'   => $mark->time,
				'memory' => $mark->memory,
				'html'   => $htmlMark,
				'tip'    => $mark->label,
			);
		}

		$avgTime = $totalTime / max(count($marks), 1);
		$avgMem  = $totalMem / max(count($marks), 1);

		foreach ($marks as $mark)
		{
			if ($mark->time > $avgTime * 1.5)
			{
				$barClass   = 'bar-danger';
				$labelClass = 'label-important label-danger';
			}
			elseif ($mark->time < $avgTime / 1.5)
			{
				$barClass   = 'bar-success';
				$labelClass = 'label-success';
			}
			else
			{
				$barClass   = 'bar-warning';
				$labelClass = 'label-warning';
			}

			if ($mark->memory > $avgMem * 1.5)
			{
				$barClassMem   = 'bar-danger';
				$labelClassMem = 'label-important label-danger';
			}
			elseif ($mark->memory < $avgMem / 1.5)
			{
				$barClassMem   = 'bar-success';
				$labelClassMem = 'label-success';
			}
			else
			{
				$barClassMem   = 'bar-warning';
				$labelClassMem = 'label-warning';
			}

			$barClass    .= " progress-$barClass";
			$barClassMem .= " progress-$barClassMem";

			$bars[] = (object) array(
				'width' => round($mark->time / ($totalTime / 100), 4),
				'class' => $barClass,
				'tip'   => $mark->tip . ' ' .
round($mark->time, 2) . ' ms',
			);

			$barsMem[] = (object) array(
				'width' => round((float) $mark->memory / ($totalMem /
100), 4),
				'class' => $barClassMem,
				'tip'   => $mark->tip . ' ' .
round($mark->memory, 3) . '  MB',
			);

			$htmlMarks[] = '<div>' .
str_replace('label-time', $labelClass,
str_replace('label-memory', $labelClassMem, $mark->html)) .
'</div>';
		}

		$html[] = '<h4>' . JText::_('PLG_DEBUG_TIME') .
'</h4>';
		$html[] = $this->renderBars($bars, 'profile');
		$html[] = '<h4>' . JText::_('PLG_DEBUG_MEMORY')
. '</h4>';
		$html[] = $this->renderBars($barsMem, 'profile');

		$html[] = '<div class="dbg-profile-list">' .
implode('', $htmlMarks) . '</div>';

		$db = $this->db;

		//  fix  for support custom shutdown function via
register_shutdown_function().
		$db->disconnect();

		$log = $db->getLog();

		if ($log)
		{
			$timings = $db->getTimings();

			if ($timings)
			{
				$totalQueryTime = 0.0;
				$lastStart      = null;

				foreach ($timings as $k => $v)
				{
					if (!($k % 2))
					{
						$lastStart = $v;
					}
					else
					{
						$totalQueryTime += $v - $lastStart;
					}
				}

				$totalQueryTime *= 1000;

				if ($totalQueryTime > ($totalTime * 0.25))
				{
					$labelClass = 'label-important';
				}
				elseif ($totalQueryTime < ($totalTime * 0.15))
				{
					$labelClass = 'label-success';
				}
				else
				{
					$labelClass = 'label-warning';
				}

				$html[] = '<br /><div>' . JText::sprintf(
						'PLG_DEBUG_QUERIES_TIME',
						sprintf('<span class="label ' . $labelClass .
'">%.2f&nbsp;ms</span>', $totalQueryTime)
					) . '</div>';

				if ($this->params->get('log-executed-sql', 0))
				{
					$this->writeToFile();
				}
			}
		}

		return implode('', $html);
	}

	/**
	 * Display memory usage.
	 *
	 * @return  string
	 *
	 * @since   2.5
	 */
	protected function displayMemoryUsage()
	{
		$bytes = memory_get_usage();

		return '<span class="label label-default">' .
JHtml::_('number.bytes', $bytes) . '</span>'
			. ' (<span class="label label-default">'
			. number_format($bytes, 0, JText::_('DECIMALS_SEPARATOR'),
JText::_('THOUSANDS_SEPARATOR'))
			. ' '
			. JText::_('PLG_DEBUG_BYTES')
			. '</span>)';
	}

	/**
	 * Display logged queries.
	 *
	 * @return  string
	 *
	 * @since   2.5
	 */
	protected function displayQueries()
	{
		$db  = $this->db;
		$log = $db->getLog();

		if (!$log)
		{
			return null;
		}

		$timings    = $db->getTimings();
		$callStacks = $db->getCallStacks();

		$db->setDebug(false);

		$selectQueryTypeTicker = array();
		$otherQueryTypeTicker  = array();

		$timing  = array();
		$maxtime = 0;

		if (isset($timings[0]))
		{
			$startTime         = $timings[0];
			$endTime           = $timings[count($timings) - 1];
			$totalBargraphTime = $endTime - $startTime;

			if ($totalBargraphTime > 0)
			{
				foreach ($log as $id => $query)
				{
					if (isset($timings[$id * 2 + 1]))
					{
						// Compute the query time: $timing[$k] = array( queryTime,
timeBetweenQueries ).
						$timing[$id] = array(
							($timings[$id * 2 + 1] - $timings[$id * 2]) * 1000,
							$id > 0 ? ($timings[$id * 2] - $timings[$id * 2 - 1]) * 1000 : 0,
						);
						$maxtime     = max($maxtime, $timing[$id]['0']);
					}
				}
			}
		}
		else
		{
			$startTime         = null;
			$totalBargraphTime = 1;
		}

		$bars           = array();
		$info           = array();
		$totalQueryTime = 0;
		$duplicates     = array();

		foreach ($log as $id => $query)
		{
			$did = md5($query);

			if (!isset($duplicates[$did]))
			{
				$duplicates[$did] = array();
			}

			$duplicates[$did][] = $id;

			if ($timings && isset($timings[$id * 2 + 1]))
			{
				// Compute the query time.
				$queryTime      = ($timings[$id * 2 + 1] - $timings[$id * 2]) * 1000;
				$totalQueryTime += $queryTime;

				// Run an EXPLAIN EXTENDED query on the SQL query if possible.
				$hasWarnings          = false;
				$hasWarningsInProfile = false;

				if (isset($this->explains[$id]))
				{
					$explain = $this->tableToHtml($this->explains[$id],
$hasWarnings);
				}
				else
				{
					$explain =
JText::sprintf('PLG_DEBUG_QUERY_EXPLAIN_NOT_POSSIBLE',
htmlspecialchars($query));
				}

				// Run a SHOW PROFILE query.
				$profile = '';

				if (isset($this->sqlShowProfileEach[$id]) &&
$db->getServerType() === 'mysql')
				{
					$profileTable = $this->sqlShowProfileEach[$id];
					$profile      = $this->tableToHtml($profileTable,
$hasWarningsInProfile);
				}

				// How heavy should the string length count: 0 - 1.
				$ratio     = 0.5;
				$timeScore = $queryTime / ((strlen($query) + 1) * $ratio) * 200;

				// Determine color of bargraph depending on query speed and presence of
warnings in EXPLAIN.
				if ($timeScore > 10)
				{
					$barClass   = 'bar-danger';
					$labelClass = 'label-important';
				}
				elseif ($hasWarnings || $timeScore > 5)
				{
					$barClass   = 'bar-warning';
					$labelClass = 'label-warning';
				}
				else
				{
					$barClass   = 'bar-success';
					$labelClass = 'label-success';
				}

				// Computes bargraph as follows: Position begin and end of the bar
relatively to whole execution time.
				// TODO: $prevBar is not used anywhere. Remove?
				$prevBar = $id && isset($bars[$id - 1]) ? $bars[$id - 1] : 0;

				$barPre   = round($timing[$id][1] / ($totalBargraphTime * 10), 4);
				$barWidth = round($timing[$id][0] / ($totalBargraphTime * 10), 4);
				$minWidth = 0.3;

				if ($barWidth < $minWidth)
				{
					$barPre -= ($minWidth - $barWidth);

					if ($barPre < 0)
					{
						$minWidth += $barPre;
						$barPre   = 0;
					}

					$barWidth = $minWidth;
				}

				$bars[$id] = (object) array(
					'class' => $barClass,
					'width' => $barWidth,
					'pre'   => $barPre,
					'tip'   => sprintf('%.2f ms', $queryTime),
				);
				$info[$id] = (object) array(
					'class'       => $labelClass,
					'explain'     => $explain,
					'profile'     => $profile,
					'hasWarnings' => $hasWarnings,
				);
			}
		}

		// Remove single queries from $duplicates.
		$total_duplicates = 0;

		foreach ($duplicates as $did => $dups)
		{
			if (count($dups) < 2)
			{
				unset($duplicates[$did]);
			}
			else
			{
				$total_duplicates += count($dups);
			}
		}

		// Fix first bar width.
		$minWidth = 0.3;

		if ($bars[0]->width < $minWidth && isset($bars[1]))
		{
			$bars[1]->pre -= ($minWidth - $bars[0]->width);

			if ($bars[1]->pre < 0)
			{
				$minWidth     += $bars[1]->pre;
				$bars[1]->pre = 0;
			}

			$bars[0]->width = $minWidth;
		}

		$memoryUsageNow = memory_get_usage();
		$list           = array();

		foreach ($log as $id => $query)
		{
			// Start query type ticker additions.
			$fromStart  = stripos($query, 'from');
			$whereStart = stripos($query, 'where', $fromStart);

			if ($whereStart === false)
			{
				$whereStart = stripos($query, 'order by', $fromStart);
			}

			if ($whereStart === false)
			{
				$whereStart = strlen($query) - 1;
			}

			$fromString = substr($query, 0, $whereStart);
			$fromString = str_replace(array("\t", "\n"), '
', $fromString);
			$fromString = trim($fromString);

			// Initialise the select/other query type counts the first time.
			if (!isset($selectQueryTypeTicker[$fromString]))
			{
				$selectQueryTypeTicker[$fromString] = 0;
			}

			if (!isset($otherQueryTypeTicker[$fromString]))
			{
				$otherQueryTypeTicker[$fromString] = 0;
			}

			// Increment the count.
			if (stripos($query, 'select') === 0)
			{
				$selectQueryTypeTicker[$fromString]++;
				unset($otherQueryTypeTicker[$fromString]);
			}
			else
			{
				$otherQueryTypeTicker[$fromString]++;
				unset($selectQueryTypeTicker[$fromString]);
			}

			$text = $this->highlightQuery($query);

			if ($timings && isset($timings[$id * 2 + 1]))
			{
				// Compute the query time.
				$queryTime = ($timings[$id * 2 + 1] - $timings[$id * 2]) * 1000;

				// Timing
				// Formats the output for the query time with EXPLAIN query results as
tooltip:
				$htmlTiming = '<div style="margin: 0 0
5px;"><span class="dbg-query-time">';
				$htmlTiming .= JText::sprintf(
					'PLG_DEBUG_QUERY_TIME',
					sprintf(
						'<span class="label
%s">%.2f&nbsp;ms</span>',
						$info[$id]->class,
						$timing[$id]['0']
					)
				);

				if ($timing[$id]['1'])
				{
					$htmlTiming .= ' ' . JText::sprintf(
							'PLG_DEBUG_QUERY_AFTER_LAST',
							sprintf('<span class="label
label-default">%.2f&nbsp;ms</span>',
$timing[$id]['1'])
						);
				}

				$htmlTiming .= '</span>';

				if (isset($callStacks[$id][0]['memory']))
				{
					$memoryUsed        = $callStacks[$id][0]['memory'][1] -
$callStacks[$id][0]['memory'][0];
					$memoryBeforeQuery = $callStacks[$id][0]['memory'][0];

					// Determine colour of query memory usage.
					if ($memoryUsed > 0.1 * $memoryUsageNow)
					{
						$labelClass = 'label-important';
					}
					elseif ($memoryUsed > 0.05 * $memoryUsageNow)
					{
						$labelClass = 'label-warning';
					}
					else
					{
						$labelClass = 'label-success';
					}

					$htmlTiming .= ' ' . '<span
class="dbg-query-memory">'
						. JText::sprintf(
							'PLG_DEBUG_MEMORY_USED_FOR_QUERY',
							sprintf('<span class="label ' . $labelClass .
'">%.3f&nbsp;MB</span>', $memoryUsed /
1048576),
							sprintf('<span class="label
label-default">%.3f&nbsp;MB</span>',
$memoryBeforeQuery / 1048576)
						)
						. '</span>';

					if ($callStacks[$id][0]['memory'][2] !== null)
					{
						// Determine colour of number or results.
						$resultsReturned = $callStacks[$id][0]['memory'][2];

						if ($resultsReturned > 3000)
						{
							$labelClass = 'label-important';
						}
						elseif ($resultsReturned > 1000)
						{
							$labelClass = 'label-warning';
						}
						elseif ($resultsReturned == 0)
						{
							$labelClass = '';
						}
						else
						{
							$labelClass = 'label-success';
						}

						$htmlResultsReturned = '<span class="label ' .
$labelClass . '">' . (int) $resultsReturned .
'</span>';
						$htmlTiming          .= ' <span
class="dbg-query-rowsnumber">'
							. JText::sprintf('PLG_DEBUG_ROWS_RETURNED_BY_QUERY',
$htmlResultsReturned) . '</span>';
					}
				}

				$htmlTiming .= '</div>';

				// Bar.
				$htmlBar = $this->renderBars($bars, 'query', $id);

				// Profile query.
				$title = JText::_('PLG_DEBUG_PROFILE');

				if (!$info[$id]->profile)
				{
					$title = '<span class="dbg-noprofile">' .
$title . '</span>';
				}

				$htmlProfile = $info[$id]->profile ?:
JText::_('PLG_DEBUG_NO_PROFILE');

				$htmlAccordions = JHtml::_(
					'bootstrap.startAccordion', 'dbg_query_' . $id,
array(
						'active' => $info[$id]->hasWarnings ?
('dbg_query_explain_' . $id) : '',
					)
				);

				$htmlAccordions .= JHtml::_('bootstrap.addSlide',
'dbg_query_' . $id, JText::_('PLG_DEBUG_EXPLAIN'),
'dbg_query_explain_' . $id)
					. $info[$id]->explain
					. JHtml::_('bootstrap.endSlide');

				$htmlAccordions .= JHtml::_('bootstrap.addSlide',
'dbg_query_' . $id, $title, 'dbg_query_profile_' . $id)
					. $htmlProfile
					. JHtml::_('bootstrap.endSlide');

				// Call stack and back trace.
				if (isset($callStacks[$id]))
				{
					$htmlAccordions .= JHtml::_('bootstrap.addSlide',
'dbg_query_' . $id, JText::_('PLG_DEBUG_CALL_STACK'),
'dbg_query_callstack_' . $id)
						. $this->renderCallStack($callStacks[$id])
						. JHtml::_('bootstrap.endSlide');
				}

				$htmlAccordions .= JHtml::_('bootstrap.endAccordion');

				$did = md5($query);

				if (isset($duplicates[$did]))
				{
					$dups = array();

					foreach ($duplicates[$did] as $dup)
					{
						if ($dup != $id)
						{
							$dups[] = '<a class="alert-link"
href="#dbg-query-' . ($dup + 1) . '">#' . ($dup
+ 1) . '</a>';
						}
					}

					$htmlQuery = '<div class="alert
alert-error">' .
JText::_('PLG_DEBUG_QUERY_DUPLICATES') . ': ' .
implode('&nbsp; ', $dups) . '</div>'
						. '<pre class="alert" title="' .
htmlspecialchars(JText::_('PLG_DEBUG_QUERY_DUPLICATES_FOUND'),
ENT_COMPAT, 'UTF-8') . '">' . $text .
'</pre>';
				}
				else
				{
					$htmlQuery = '<pre>' . $text .
'</pre>';
				}

				$list[] = '<a name="dbg-query-' . ($id + 1) .
'"></a>'
					. $htmlTiming
					. $htmlBar
					. $htmlQuery
					. $htmlAccordions;
			}
			else
			{
				$list[] = '<pre>' . $text . '</pre>';
			}
		}

		$totalTime = 0;

		foreach (JProfiler::getInstance('Application')->getMarks()
as $mark)
		{
			$totalTime += $mark->time;
		}

		if ($totalQueryTime > ($totalTime * 0.25))
		{
			$labelClass = 'label-important';
		}
		elseif ($totalQueryTime < ($totalTime * 0.15))
		{
			$labelClass = 'label-success';
		}
		else
		{
			$labelClass = 'label-warning';
		}

		if ($this->totalQueries === 0)
		{
			$this->totalQueries = $db->getCount();
		}

		$html = array();

		$html[] = '<h4>' .
JText::sprintf('PLG_DEBUG_QUERIES_LOGGED',
$this->totalQueries)
			. sprintf(' <span class="label ' . $labelClass .
'">%.2f&nbsp;ms</span>', $totalQueryTime) .
'</h4><br />';

		if ($total_duplicates)
		{
			$html[] = '<div class="alert alert-error">'
				. '<h4>' .
JText::sprintf('PLG_DEBUG_QUERY_DUPLICATES_TOTAL_NUMBER',
$total_duplicates) . '</h4>';

			foreach ($duplicates as $dups)
			{
				$links = array();

				foreach ($dups as $dup)
				{
					$links[] = '<a class="alert-link"
href="#dbg-query-' . ($dup + 1) . '">#' . ($dup
+ 1) . '</a>';
				}

				$html[] = '<div>' .
JText::sprintf('PLG_DEBUG_QUERY_DUPLICATES_NUMBER',
count($links)) . ': ' . implode('&nbsp; ', $links)
. '</div>';
			}

			$html[] = '</div>';
		}

		$html[] = '<ol><li>' . implode('<hr
/></li><li>', $list) . '<hr
/></li></ol>';

		if (!$this->params->get('query_types', 1))
		{
			return implode('', $html);
		}

		// Get the totals for the query types.
		$totalSelectQueryTypes = count($selectQueryTypeTicker);
		$totalOtherQueryTypes  = count($otherQueryTypeTicker);
		$totalQueryTypes       = $totalSelectQueryTypes + $totalOtherQueryTypes;

		$html[] = '<h4>' .
JText::sprintf('PLG_DEBUG_QUERY_TYPES_LOGGED', $totalQueryTypes)
. '</h4>';

		if ($totalSelectQueryTypes)
		{
			$html[] = '<h5>' .
JText::_('PLG_DEBUG_SELECT_QUERIES') . '</h5>';

			arsort($selectQueryTypeTicker);

			$list = array();

			foreach ($selectQueryTypeTicker as $query => $occurrences)
			{
				$list[] = '<pre>'
					. JText::sprintf('PLG_DEBUG_QUERY_TYPE_AND_OCCURRENCES',
$this->highlightQuery($query), $occurrences)
					. '</pre>';
			}

			$html[] = '<ol><li>' .
implode('</li><li>', $list) .
'</li></ol>';
		}

		if ($totalOtherQueryTypes)
		{
			$html[] = '<h5>' .
JText::_('PLG_DEBUG_OTHER_QUERIES') . '</h5>';

			arsort($otherQueryTypeTicker);

			$list = array();

			foreach ($otherQueryTypeTicker as $query => $occurrences)
			{
				$list[] = '<pre>'
					. JText::sprintf('PLG_DEBUG_QUERY_TYPE_AND_OCCURRENCES',
$this->highlightQuery($query), $occurrences)
					. '</pre>';
			}

			$html[] = '<ol><li>' .
implode('</li><li>', $list) .
'</li></ol>';
		}

		return implode('', $html);
	}

	/**
	 * Render the bars.
	 *
	 * @param   array    &$bars  Array of bar data
	 * @param   string   $class  Optional class for items
	 * @param   integer  $id     Id if the bar to highlight
	 *
	 * @return  string
	 *
	 * @since   3.1.2
	 */
	protected function renderBars(&$bars, $class = '', $id =
null)
	{
		$html = array();

		foreach ($bars as $i => $bar)
		{
			if (isset($bar->pre) && $bar->pre)
			{
				$html[] = '<div class="dbg-bar-spacer"
style="width:' . $bar->pre .
'%;"></div>';
			}

			$barClass = trim('bar dbg-bar progress-bar ' .
(isset($bar->class) ? $bar->class : ''));

			if ($id !== null && $i == $id)
			{
				$barClass .= ' dbg-bar-active';
			}

			$tip = empty($bar->tip) ? '' : ' title="' .
htmlspecialchars($bar->tip, ENT_COMPAT, 'UTF-8') .
'"';

			$html[] = '<a class="bar dbg-bar ' . $barClass .
'"' . $tip . ' style="width: '
				. $bar->width . '%;" href="#dbg-' . $class .
'-' . ($i + 1) . '"></a>';
		}

		return '<div class="progress dbg-bars dbg-bars-' .
$class . '">' . implode('', $html) .
'</div>';
	}

	/**
	 * Render an HTML table based on a multi-dimensional array.
	 *
	 * @param   array    $table         An array of tabular data.
	 * @param   boolean  &$hasWarnings  Changes value to true if warnings
are displayed, otherwise untouched
	 *
	 * @return  string
	 *
	 * @since   3.1.2
	 */
	protected function tableToHtml($table, &$hasWarnings)
	{
		if (!$table)
		{
			return null;
		}

		$html = array();

		$html[] = '<table class="table table-striped
dbg-query-table">';
		$html[] = '<thead>';
		$html[] = '<tr>';

		foreach (array_keys($table[0]) as $k)
		{
			$html[] = '<th>' . htmlspecialchars($k) .
'</th>';
		}

		$html[]    = '</tr>';
		$html[]    = '</thead>';
		$html[]    = '<tbody>';
		$durations = array();

		foreach ($table as $tr)
		{
			if (isset($tr['Duration']))
			{
				$durations[] = $tr['Duration'];
			}
		}

		rsort($durations, SORT_NUMERIC);

		foreach ($table as $tr)
		{
			$html[] = '<tr>';

			foreach ($tr as $k => $td)
			{
				if ($td === null)
				{
					// Display null's as 'NULL'.
					$td = 'NULL';
				}

				// Treat special columns.
				if ($k === 'Duration')
				{
					if ($td >= 0.001 && ($td == $durations[0] ||
(isset($durations[1]) && $td == $durations[1])))
					{
						// Duration column with duration value of more than 1 ms and within 2
top duration in SQL engine: Highlight warning.
						$html[]      = '<td class="dbg-warning">';
						$hasWarnings = true;
					}
					else
					{
						$html[] = '<td>';
					}

					// Display duration in milliseconds with the unit instead of seconds.
					$html[] = sprintf('%.2f&nbsp;ms', $td * 1000);
				}
				elseif ($k === 'Error')
				{
					// An error in the EXPLAIN query occurred, display it instead of the
result (means original query had syntax error most probably).
					$html[]      = '<td class="dbg-warning">' .
htmlspecialchars($td);
					$hasWarnings = true;
				}
				elseif ($k === 'key')
				{
					if ($td === 'NULL')
					{
						// Displays query parts which don't use a key with warning:
						$html[]      = '<td><strong>' . '<span
class="dbg-warning" title="'
							.
htmlspecialchars(JText::_('PLG_DEBUG_WARNING_NO_INDEX_DESC'),
ENT_COMPAT, 'UTF-8') . '">'
							. JText::_('PLG_DEBUG_WARNING_NO_INDEX') .
'</span>' . '</strong>';
						$hasWarnings = true;
					}
					else
					{
						$html[] = '<td><strong>' .
htmlspecialchars($td) . '</strong>';
					}
				}
				elseif ($k === 'Extra')
				{
					$htmlTd = htmlspecialchars($td);

					// Replace spaces with &nbsp; (non-breaking spaces) for less tall
tables displayed.
					$htmlTd = preg_replace('/([^;]) /',
'\1&nbsp;', $htmlTd);

					// Displays warnings for "Using filesort":
					$htmlTdWithWarnings = str_replace(
						'Using&nbsp;filesort',
						'<span class="dbg-warning" title="'
						.
htmlspecialchars(JText::_('PLG_DEBUG_WARNING_USING_FILESORT_DESC'),
ENT_COMPAT, 'UTF-8') . '">'
						. JText::_('PLG_DEBUG_WARNING_USING_FILESORT') .
'</span>',
						$htmlTd
					);

					if ($htmlTdWithWarnings !== $htmlTd)
					{
						$hasWarnings = true;
					}

					$html[] = '<td>' . $htmlTdWithWarnings;
				}
				else
				{
					$html[] = '<td>' . htmlspecialchars($td);
				}

				$html[] = '</td>';
			}

			$html[] = '</tr>';
		}

		$html[] = '</tbody>';
		$html[] = '</table>';

		return implode('', $html);
	}

	/**
	 * Disconnect handler for database to collect profiling and explain
information.
	 *
	 * @param   JDatabaseDriver  &$db  Database object.
	 *
	 * @return  void
	 *
	 * @since   3.1.2
	 */
	public function mysqlDisconnectHandler(&$db)
	{
		$db->setDebug(false);

		$this->totalQueries = $db->getCount();

		$dbVersion5037 = $db->getServerType() === 'mysql' &&
version_compare($db->getVersion(), '5.0.37',
'>=');

		if ($dbVersion5037)
		{
			try
			{
				// Check if profiling is enabled.
				$db->setQuery("SHOW VARIABLES LIKE
'have_profiling'");
				$hasProfiling = $db->loadResult();

				if ($hasProfiling)
				{
					// Run a SHOW PROFILE query.
					$db->setQuery('SHOW PROFILES');
					$this->sqlShowProfiles = $db->loadAssocList();

					if ($this->sqlShowProfiles)
					{
						foreach ($this->sqlShowProfiles as $qn)
						{
							// Run SHOW PROFILE FOR QUERY for each query where a profile is
available (max 100).
							$db->setQuery('SHOW PROFILE FOR QUERY ' . (int)
$qn['Query_ID']);
							$this->sqlShowProfileEach[(int) ($qn['Query_ID'] - 1)]
= $db->loadAssocList();
						}
					}
				}
				else
				{
					$this->sqlShowProfileEach[0] = array(array('Error' =>
'MySql have_profiling = off'));
				}
			}
			catch (Exception $e)
			{
				$this->sqlShowProfileEach[0] = array(array('Error' =>
$e->getMessage()));
			}
		}

		if (in_array($db->getServerType(), array('mysql',
'postgresql'), true))
		{
			$log = $db->getLog();

			foreach ($log as $k => $query)
			{
				$dbVersion56 = $db->getServerType() === 'mysql' &&
version_compare($db->getVersion(), '5.6', '>=');
				$dbVersion80 = $db->getServerType() === 'mysql' &&
version_compare($db->getVersion(), '8.0', '>=');

				if ($dbVersion80)
				{
					$dbVersion56 = false;
				}

				if ((stripos($query, 'select') === 0) || ($dbVersion56
&& ((stripos($query, 'delete') === 0) || (stripos($query,
'update') === 0))))
				{
					try
					{
						$db->setQuery('EXPLAIN ' . ($dbVersion56 ?
'EXTENDED ' : '') . $query);
						$this->explains[$k] = $db->loadAssocList();
					}
					catch (Exception $e)
					{
						$this->explains[$k] = array(array('Error' =>
$e->getMessage()));
					}
				}
			}
		}
	}

	/**
	 * Displays errors in language files.
	 *
	 * @return  string
	 *
	 * @since   2.5
	 */
	protected function displayLanguageFilesInError()
	{
		$errorfiles = JFactory::getLanguage()->getErrorFiles();

		if (!count($errorfiles))
		{
			return '<p>' . JText::_('JNONE') .
'</p>';
		}

		$html = array();

		$html[] = '<ul>';

		foreach ($errorfiles as $file => $error)
		{
			$html[] = '<li>' . $this->formatLink($file) .
str_replace($file, '', $error) . '</li>';
		}

		$html[] = '</ul>';

		return implode('', $html);
	}

	/**
	 * Display loaded language files.
	 *
	 * @return  string
	 *
	 * @since   2.5
	 */
	protected function displayLanguageFilesLoaded()
	{
		$html = array();

		$html[] = '<ul>';

		foreach (JFactory::getLanguage()->getPaths() as /* $extension => */
$files)
		{
			foreach ($files as $file => $status)
			{
				$html[] = '<li>';

				$html[] = $status
					? JText::_('PLG_DEBUG_LANG_LOADED')
					: JText::_('PLG_DEBUG_LANG_NOT_LOADED');

				$html[] = ' : ';
				$html[] = $this->formatLink($file);
				$html[] = '</li>';
			}
		}

		$html[] = '</ul>';

		return implode('', $html);
	}

	/**
	 * Display untranslated language strings.
	 *
	 * @return  string
	 *
	 * @since   2.5
	 */
	protected function displayUntranslatedStrings()
	{
		$stripFirst = $this->params->get('strip-first', 1);
		$stripPref  = $this->params->get('strip-prefix');
		$stripSuff  = $this->params->get('strip-suffix');

		$orphans = JFactory::getLanguage()->getOrphans();

		if (!count($orphans))
		{
			return '<p>' . JText::_('JNONE') .
'</p>';
		}

		ksort($orphans, SORT_STRING);

		$guesses = array();

		foreach ($orphans as $key => $occurance)
		{
			if (is_array($occurance) && isset($occurance[0]))
			{
				$info = $occurance[0];
				$file = $info['file'] ?: '';

				if (!isset($guesses[$file]))
				{
					$guesses[$file] = array();
				}

				// Prepare the key.
				if (($pos = strpos($info['string'], '=')) > 0)
				{
					$parts = explode('=', $info['string']);
					$key   = $parts[0];
					$guess = $parts[1];
				}
				else
				{
					$guess = str_replace('_', ' ',
$info['string']);

					if ($stripFirst)
					{
						$parts = explode(' ', $guess);

						if (count($parts) > 1)
						{
							array_shift($parts);
							$guess = implode(' ', $parts);
						}
					}

					$guess = trim($guess);

					if ($stripPref)
					{
						$guess = trim(preg_replace(chr(1) . '^' . $stripPref .
chr(1) . 'i', '', $guess));
					}

					if ($stripSuff)
					{
						$guess = trim(preg_replace(chr(1) . $stripSuff . '$' .
chr(1) . 'i', '', $guess));
					}
				}

				$key = strtoupper(trim($key));
				$key = preg_replace('#\s+#', '_', $key);
				$key = preg_replace('#\W#', '', $key);

				// Prepare the text.
				$guesses[$file][] = $key . '="' . $guess .
'"';
			}
		}

		$html = array();

		foreach ($guesses as $file => $keys)
		{
			$html[] = "\n\n# " . ($file ? $this->formatLink($file) :
JText::_('PLG_DEBUG_UNKNOWN_FILE')) . "\n\n";
			$html[] = implode("\n", $keys);
		}

		return '<pre>' . implode('', $html) .
'</pre>';
	}

	/**
	 * Simple highlight for SQL queries.
	 *
	 * @param   string  $query  The query to highlight.
	 *
	 * @return  string
	 *
	 * @since   2.5
	 */
	protected function highlightQuery($query)
	{
		$newlineKeywords =
'#\b(FROM|LEFT|INNER|OUTER|WHERE|SET|VALUES|ORDER|GROUP|HAVING|LIMIT|ON|AND|CASE)\b#i';

		$query = htmlspecialchars($query, ENT_QUOTES);

		$query = preg_replace($newlineKeywords, '<br
/>&#160;&#160;\\0', $query);

		$regex = array(

			// Tables are identified by the prefix.
			'/(=)/'                                        =>
'<b class="dbg-operator">$1</b>',

			// All uppercase words have a special meaning.
			'/(?<!\w|>)([A-Z_]{2,})(?!\w)/x'               =>
'<span class="dbg-command">$1</span>',

			// Tables are identified by the prefix.
			'/(' . $this->db->getPrefix() . '[a-z_0-9]+)/'
=> '<span class="dbg-table">$1</span>',

		);

		$query = preg_replace(array_keys($regex), array_values($regex), $query);

		$query = str_replace('*', '<b style="color:
red;">*</b>', $query);

		return $query;
	}

	/**
	 * Render the backtrace.
	 *
	 * Stolen from JError to prevent it's removal.
	 *
	 * @param   Exception  $error  The Exception object to be rendered.
	 *
	 * @return  string     Rendered backtrace.
	 *
	 * @since   2.5
	 */
	protected function renderBacktrace($error)
	{
		return JLayoutHelper::render('joomla.error.backtrace',
array('backtrace' => $error->getTrace()));
	}

	/**
	 * Replaces the Joomla! root with "JROOT" to improve
readability.
	 * Formats a link with a special value xdebug.file_link_format
	 * from the php.ini file.
	 *
	 * @param   string  $file  The full path to the file.
	 * @param   string  $line  The line number.
	 *
	 * @return  string
	 *
	 * @since   2.5
	 */
	protected function formatLink($file, $line = '')
	{
		return JHtml::_('debug.xdebuglink', $file, $line);
	}

	/**
	 * Store log messages so they can be displayed later.
	 * This function is passed log entries by JLogLoggerCallback.
	 *
	 * @param   JLogEntry  $entry  A log entry.
	 *
	 * @return  void
	 *
	 * @since   3.1
	 */
	public function logger(JLogEntry $entry)
	{
		$this->logEntries[] = $entry;
	}

	/**
	 * Display log messages.
	 *
	 * @return  string
	 *
	 * @since   3.1
	 */
	protected function displayLogs()
	{
		$priorities = array(
			JLog::EMERGENCY => '<span class="badge
badge-important">EMERGENCY</span>',
			JLog::ALERT     => '<span class="badge
badge-important">ALERT</span>',
			JLog::CRITICAL  => '<span class="badge
badge-important">CRITICAL</span>',
			JLog::ERROR     => '<span class="badge
badge-important">ERROR</span>',
			JLog::WARNING   => '<span class="badge
badge-warning">WARNING</span>',
			JLog::NOTICE    => '<span class="badge
badge-info">NOTICE</span>',
			JLog::INFO      => '<span class="badge
badge-info">INFO</span>',
			JLog::DEBUG     => '<span
class="badge">DEBUG</span>',
		);

		$out = '';

		$logEntriesTotal = count($this->logEntries);

		// SQL log entries
		$showExecutedSQL = $this->params->get('log-executed-sql',
0);

		if (!$showExecutedSQL)
		{
			$logEntriesDatabasequery = count(
				array_filter(
					$this->logEntries, function ($logEntry)
					{
						return $logEntry->category === 'databasequery';
					}
				)
			);
			$logEntriesTotal         -= $logEntriesDatabasequery;
		}

		// Deprecated log entries
		$logEntriesDeprecated = count(
			array_filter(
				$this->logEntries, function ($logEntry)
				{
					return $logEntry->category === 'deprecated';
				}
			)
		);
		$showDeprecated       =
$this->params->get('log-deprecated', 0);

		if (!$showDeprecated)
		{
			$logEntriesTotal -= $logEntriesDeprecated;
		}

		$showEverything = $this->params->get('log-everything',
0);

		$out .= '<h4>' .
JText::sprintf('PLG_DEBUG_LOGS_LOGGED', $logEntriesTotal) .
'</h4><br />';

		if ($showDeprecated && $logEntriesDeprecated > 0)
		{
			$out .= '
			<div class="alert alert-warning">
				<h4>' .
JText::sprintf('PLG_DEBUG_LOGS_DEPRECATED_FOUND_TITLE',
$logEntriesDeprecated) . '</h4>
				<div>' .
JText::_('PLG_DEBUG_LOGS_DEPRECATED_FOUND_TEXT') .
'</div>
			</div>
			<br />';
		}

		$out   .= '<ol>';
		$count = 1;

		foreach ($this->logEntries as $entry)
		{
			// Don't show database queries if not selected.
			if (!$showExecutedSQL && $entry->category ===
'databasequery')
			{
				continue;
			}

			// Don't show deprecated logs if not selected.
			if (!$showDeprecated && $entry->category ===
'deprecated')
			{
				continue;
			}

			// Don't show everything logs if not selected.
			if (!$showEverything && !in_array($entry->category,
array('deprecated', 'databasequery'), true))
			{
				continue;
			}

			$out .= '<li id="dbg_logs_' . $count .
'">';
			$out .= '<h5>' . $priorities[$entry->priority] .
' ' . $entry->category . '</h5><br />
				<pre>' . $entry->message . '</pre>';

			if ($entry->callStack)
			{
				$out .= JHtml::_('bootstrap.startAccordion',
'dbg_logs_' . $count, array('active' =>
''));
				$out .= JHtml::_('bootstrap.addSlide', 'dbg_logs_'
. $count, JText::_('PLG_DEBUG_CALL_STACK'),
'dbg_logs_backtrace_' . $count);
				$out .= $this->renderCallStack($entry->callStack);
				$out .= JHtml::_('bootstrap.endSlide');
				$out .= JHtml::_('bootstrap.endAccordion');
			}

			$out .= '<hr /></li>';
			$count++;
		}

		$out .= '</ol>';

		return $out;
	}

	/**
	 * Renders call stack and back trace in HTML.
	 *
	 * @param   array  $callStack  The call stack and back trace array.
	 *
	 * @return  string  The call stack and back trace in HMTL format.
	 *
	 * @since   3.5
	 */
	protected function renderCallStack(array $callStack = array())
	{
		$htmlCallStack = '';

		if ($callStack !== null)
		{
			$htmlCallStack .= '<div>';
			$htmlCallStack .= '<table class="table table-striped
dbg-query-table">';
			$htmlCallStack .= '<thead>';
			$htmlCallStack .= '<tr>';
			$htmlCallStack .= '<th>#</th>';
			$htmlCallStack .= '<th>' .
JText::_('PLG_DEBUG_CALL_STACK_CALLER') .
'</th>';
			$htmlCallStack .= '<th>' .
JText::_('PLG_DEBUG_CALL_STACK_FILE_AND_LINE') .
'</th>';
			$htmlCallStack .= '</tr>';
			$htmlCallStack .= '</thead>';
			$htmlCallStack .= '<tbody>';

			$count = count($callStack);

			foreach ($callStack as $call)
			{
				// Dont' back trace log classes.
				if (isset($call['class']) &&
strpos($call['class'], 'JLog') !== false)
				{
					$count--;
					continue;
				}

				$htmlCallStack .= '<tr>';

				$htmlCallStack .= '<td>' . $count .
'</td>';

				$htmlCallStack .= '<td>';

				if (isset($call['class']))
				{
					// If entry has Class/Method print it.
					$htmlCallStack .= htmlspecialchars($call['class'] .
$call['type'] . $call['function']) . '()';
				}
				else
				{
					if (isset($call['args']))
					{
						// If entry has args is a require/include.
						$htmlCallStack .= htmlspecialchars($call['function']) .
' ' . $this->formatLink($call['args'][0]);
					}
					else
					{
						// It's a function.
						$htmlCallStack .= htmlspecialchars($call['function']) .
'()';
					}
				}

				$htmlCallStack .= '</td>';

				$htmlCallStack .= '<td>';

				// If entry doesn't have line and number the next is a
call_user_func.
				if (!isset($call['file']) &&
!isset($call['line']))
				{
					$htmlCallStack .=
JText::_('PLG_DEBUG_CALL_STACK_SAME_FILE');
				}
				// If entry has file and line print it.
				else
				{
					$htmlCallStack .=
$this->formatLink(htmlspecialchars($call['file']),
htmlspecialchars($call['line']));
				}

				$htmlCallStack .= '</td>';

				$htmlCallStack .= '</tr>';
				$count--;
			}

			$htmlCallStack .= '</tbody>';
			$htmlCallStack .= '</table>';
			$htmlCallStack .= '</div>';

			if (!$this->linkFormat)
			{
				$htmlCallStack .= '<div>[<a
href="https://xdebug.org/docs/all_settings#file_link_format"
target="_blank" rel="noopener noreferrer">';
				$htmlCallStack .= JText::_('PLG_DEBUG_LINK_FORMAT') .
'</a>]</div>';
			}
		}

		return $htmlCallStack;
	}

	/**
	 * Pretty print JSON with colors.
	 *
	 * @param   string  $json  The json raw string.
	 *
	 * @return  string  The json string pretty printed.
	 *
	 * @since   3.5
	 */
	protected function prettyPrintJSON($json = '')
	{
		// In PHP 5.4.0 or later we have pretty print option.
		if (version_compare(PHP_VERSION, '5.4', '>='))
		{
			$json = json_encode($json, JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT);
		}

		// Escape HTML in session vars
		$json = htmlentities($json);

		// Add some colors
		$json = preg_replace('#"([^"]+)":#',
'<span class=\'black\'>"</span><span
class=\'green\'>$1</span><span
class=\'black\'>"</span>:', $json);
		$json = preg_replace('#"(|[^"]+)"(\n|\r\n|,)#',
'<span
class=\'grey\'>"$1"</span>$2', $json);
		$json = str_replace('null,', '<span
class=\'blue\'>null</span>,', $json);

		return $json;
	}

	/**
	 * Write query to the log file
	 *
	 * @return  void
	 *
	 * @since   3.5
	 */
	protected function writeToFile()
	{
		$app    = JFactory::getApplication();
		$domain = $app->isClient('site') ? 'site' :
'admin';
		$input  = $app->input;
		$file   = $app->get('log_path') . '/' . $domain .
'_' . $input->get('option') .
$input->get('view') . $input->get('layout') .
'.sql.php';

		// Get the queries from log.
		$current = '';
		$db      = $this->db;
		$log     = $db->getLog();
		$timings = $db->getTimings();

		foreach ($log as $id => $query)
		{
			if (isset($timings[$id * 2 + 1]))
			{
				$temp    = str_replace('`', '', $log[$id]);
				$temp    = str_replace(array("\t", "\n",
"\r\n"), ' ', $temp);
				$current .= $temp . ";\n";
			}
		}

		if (JFile::exists($file))
		{
			JFile::delete($file);
		}

		$head   = array('#');
		$head[] = '#<?php die(\'Forbidden.\'); ?>';
		$head[] = '#Date: ' . gmdate('Y-m-d H:i:s') . '
UTC';
		$head[] = '#Software: ' . \JPlatform::getLongVersion();
		$head[] = "\n";

		// Write new file.
		JFile::write($file, implode("\n", $head) . $current);
	}
}
PK|��[�i�>>debug/debug.xmlnu�[���<?xml
version="1.0" encoding="utf-8"?>
<extension version="3.1" type="plugin"
group="system" method="upgrade">
	<name>plg_system_debug</name>
	<author>Joomla! Project</author>
	<creationDate>December 2006</creationDate>
	<copyright>(C) 2006 Open Source Matters, Inc.</copyright>
	<license>GNU General Public License version 2 or later; see
LICENSE.txt</license>
	<authorEmail>admin@joomla.org</authorEmail>
	<authorUrl>www.joomla.org</authorUrl>
	<version>3.0.0</version>
	<description>PLG_DEBUG_XML_DESCRIPTION</description>
	<files>
		<filename plugin="debug">debug.php</filename>
	</files>
	<languages>
		<language
tag="en-GB">en-GB.plg_system_debug.ini</language>
		<language
tag="en-GB">en-GB.plg_system_debug.sys.ini</language>
	</languages>
	<config>
		<fields name="params">
			<fieldset name="basic">
				<field
					name="filter_groups"
					type="usergrouplist"
					label="PLG_DEBUG_FIELD_ALLOWED_GROUPS_LABEL"
					description="PLG_DEBUG_FIELD_ALLOWED_GROUPS_DESC"
					multiple="true"
					filter="int_array"
					size="10"
				/>

				<field
					name="session"
					type="radio"
					label="PLG_DEBUG_FIELD_SESSION_LABEL"
					description="PLG_DEBUG_FIELD_SESSION_DESC"
					class="btn-group btn-group-yesno"
					default="1"
					filter="integer"
					>
					<option value="1">JYES</option>
					<option value="0">JNO</option>
				</field>

				<field
					name="profile"
					type="radio"
					label="PLG_DEBUG_FIELD_PROFILING_LABEL"
					description="PLG_DEBUG_FIELD_PROFILING_DESC"
					class="btn-group btn-group-yesno"
					default="1"
					filter="integer"
					>
					<option value="1">JYES</option>
					<option value="0">JNO</option>
				</field>

				<field
					name="queries"
					type="radio"
					label="PLG_DEBUG_FIELD_QUERIES_LABEL"
					description="PLG_DEBUG_FIELD_QUERIES_DESC"
					class="btn-group btn-group-yesno"
					default="1"
					filter="integer"
					>
					<option value="1">JYES</option>
					<option value="0">JNO</option>
				</field>

				<field
					name="query_types"
					type="radio"
					label="PLG_DEBUG_FIELD_QUERY_TYPES_LABEL"
					description="PLG_DEBUG_FIELD_QUERY_TYPES_DESC"
					class="btn-group btn-group-yesno"
					default="1"
					filter="integer"
					>
					<option value="1">JYES</option>
					<option value="0">JNO</option>
				</field>

				<field
					name="memory"
					type="radio"
					label="PLG_DEBUG_FIELD_MEMORY_LABEL"
					description="PLG_DEBUG_FIELD_MEMORY_DESC"
					class="btn-group btn-group-yesno"
					default="1"
					filter="integer"
					>
					<option value="1">JYES</option>
					<option value="0">JNO</option>
				</field>

				<field
					name="logs"
					type="radio"
					label="PLG_DEBUG_FIELD_LOGS_LABEL"
					description="PLG_DEBUG_FIELD_LOGS_DESC"
					class="btn-group btn-group-yesno"
					default="1"
					filter="integer"
					>
					<option value="1">JYES</option>
					<option value="0">JNO</option>
				</field>

				<field
					name="log_priorities"
					type="list"
					label="PLG_DEBUG_FIELD_LOG_PRIORITIES_LABEL"
					description="PLG_DEBUG_FIELD_LOG_PRIORITIES_DESC"
					multiple="true"
					default="all"
					>
					<option
value="all">PLG_DEBUG_FIELD_LOG_PRIORITIES_ALL</option>
					<option
value="emergency">PLG_DEBUG_FIELD_LOG_PRIORITIES_EMERGENCY</option>
					<option
value="alert">PLG_DEBUG_FIELD_LOG_PRIORITIES_ALERT</option>
					<option
value="critical">PLG_DEBUG_FIELD_LOG_PRIORITIES_CRITICAL</option>
					<option
value="error">PLG_DEBUG_FIELD_LOG_PRIORITIES_ERROR</option>
					<option
value="warning">PLG_DEBUG_FIELD_LOG_PRIORITIES_WARNING</option>
					<option
value="notice">PLG_DEBUG_FIELD_LOG_PRIORITIES_NOTICE</option>
					<option
value="info">PLG_DEBUG_FIELD_LOG_PRIORITIES_INFO</option>
					<option
value="debug">PLG_DEBUG_FIELD_LOG_PRIORITIES_DEBUG</option>
				</field>

				<field
					name="log_categories"
					type="text"
					label="PLG_DEBUG_FIELD_LOG_CATEGORIES_LABEL"
					description="PLG_DEBUG_FIELD_LOG_CATEGORIES_DESC"
					size="60"
				/>

				<field
					name="log_category_mode"
					type="radio"
					label="PLG_DEBUG_FIELD_LOG_CATEGORY_MODE_LABEL"
					description="PLG_DEBUG_FIELD_LOG_CATEGORY_MODE_DESC"
					default="0"
					filter="integer"
					class="btn-group btn-group-yesno btn-group-reversed"
					>
					<option
value="0">PLG_DEBUG_FIELD_LOG_CATEGORY_MODE_INCLUDE</option>
					<option
value="1">PLG_DEBUG_FIELD_LOG_CATEGORY_MODE_EXCLUDE</option>
				</field>

				<field
					name="refresh_assets"
					type="radio"
					label="PLG_DEBUG_FIELD_REFRESH_ASSETS_LABEL"
					description="PLG_DEBUG_FIELD_REFRESH_ASSETS_DESC"
					class="btn-group btn-group-yesno"
					default="1"
					filter="integer"
					>
					<option value="1">JYES</option>
					<option value="0">JNO</option>
				</field>
			</fieldset>

			<fieldset
				name="language"
				label="PLG_DEBUG_LANGUAGE_FIELDSET_LABEL"
				>

				<field
					name="language_errorfiles"
					type="radio"
					label="PLG_DEBUG_FIELD_LANGUAGE_ERRORFILES_LABEL"
					description="PLG_DEBUG_FIELD_LANGUAGE_ERRORFILES_DESC"
					class="btn-group btn-group-yesno"
					default="1"
					filter="integer"
					>
					<option value="1">JYES</option>
					<option value="0">JNO</option>
				</field>

				<field
					name="language_files"
					type="radio"
					label="PLG_DEBUG_FIELD_LANGUAGE_FILES_LABEL"
					description="PLG_DEBUG_FIELD_LANGUAGE_FILES_DESC"
					class="btn-group btn-group-yesno"
					default="1"
					filter="integer"
					>
					<option value="1">JYES</option>
					<option value="0">JNO</option>
				</field>

				<field
					name="language_strings"
					type="radio"
					label="PLG_DEBUG_FIELD_LANGUAGE_STRING_LABEL"
					description="PLG_DEBUG_FIELD_LANGUAGE_STRING_DESC"
					class="btn-group btn-group-yesno"
					default="1"
					filter="integer"
					>
					<option value="1">JYES</option>
					<option value="0">JNO</option>
				</field>

				<field
					name="strip-first"
					type="radio"
					label="PLG_DEBUG_FIELD_STRIP_FIRST_LABEL"
					description="PLG_DEBUG_FIELD_STRIP_FIRST_DESC"
					class="btn-group btn-group-yesno"
					default="1"
					filter="integer"
					>
					<option value="1">JYES</option>
					<option value="0">JNO</option>
				</field>

				<field
					name="strip-prefix"
					type="textarea"
					label="PLG_DEBUG_FIELD_STRIP_PREFIX_LABEL"
					description="PLG_DEBUG_FIELD_STRIP_PREFIX_DESC"
					cols="30"
					rows="4"
				/>

				<field
					name="strip-suffix"
					type="textarea"
					label="PLG_DEBUG_FIELD_STRIP_SUFFIX_LABEL"
					description="PLG_DEBUG_FIELD_STRIP_SUFFIX_DESC"
					cols="30"
					rows="4"
				/>
			</fieldset>

			<fieldset
				name="logging"
				label="PLG_DEBUG_LOGGING_FIELDSET_LABEL"
				>
				<field
					name="log-deprecated"
					type="radio"
					label="PLG_DEBUG_FIELD_LOG_DEPRECATED_LABEL"
					description="PLG_DEBUG_FIELD_LOG_DEPRECATED_DESC"
					class="btn-group btn-group-yesno"
					default="0"
					filter="integer"
					>
					<option value="1">JYES</option>
					<option value="0">JNO</option>
				</field>

				<field
					name="log-everything"
					type="radio"
					label="PLG_DEBUG_FIELD_LOG_EVERYTHING_LABEL"
					description="PLG_DEBUG_FIELD_LOG_EVERYTHING_DESC"
					class="btn-group btn-group-yesno"
					default="0"
					filter="integer"
					>
					<option value="1">JYES</option>
					<option value="0">JNO</option>
				</field>

				<field
					name="log-executed-sql"
					type="radio"
					label="PLG_DEBUG_FIELD_EXECUTEDSQL_LABEL"
					description="PLG_DEBUG_FIELD_EXECUTEDSQL_DESC"
					class="btn-group btn-group-yesno"
					default="0"
					filter="integer"
					>
					<option value="1">JYES</option>
					<option value="0">JNO</option>
				</field>
			</fieldset>
		</fields>
	</config>
</extension>
PK|��[��W8�2�2fields/fields.phpnu�[���<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.Fields
 *
 * @copyright   (C) 2016 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\CMS\Form\Form;
use Joomla\Registry\Registry;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Multilanguage;

JLoader::register('FieldsHelper', JPATH_ADMINISTRATOR .
'/components/com_fields/helpers/fields.php');

/**
 * Fields Plugin
 *
 * @since  3.7
 */
class PlgSystemFields extends JPlugin
{
	/**
	 * Load the language file on instantiation.
	 *
	 * @var    boolean
	 * @since  3.7.0
	 */
	protected $autoloadLanguage = true;

	/**
	 * Normalizes the request data.
	 *
	 * @param   string  $context  The context
	 * @param   object  $data     The object
	 * @param   Form    $form     The form
	 *
	 * @return  void
	 *
	 * @since   3.8.7
	 */
	public function onContentNormaliseRequestData($context, $data, Form $form)
	{
		if (!FieldsHelper::extract($context, $data))
		{
			return true;
		}

		// Loop over all fields
		foreach ($form->getGroup('com_fields') as $field)
		{
			if ($field->disabled === true)
			{
				/**
				 * Disabled fields should NEVER be added to the request as
				 * they should NEVER be added by the browser anyway so nothing to check
against
				 * as "disabled" means no interaction at all.
				 */

				// Make sure the data object has an entry before delete it
				if (isset($data->com_fields[$field->fieldname]))
				{
					unset($data->com_fields[$field->fieldname]);
				}

				continue;
			}

			// Make sure the data object has an entry
			if (isset($data->com_fields[$field->fieldname]))
			{
				continue;
			}

			// Set a default value for the field
			$data->com_fields[$field->fieldname] = false;
		}
	}

	/**
	 * The save event.
	 *
	 * @param   string   $context  The context
	 * @param   JTable   $item     The table
	 * @param   boolean  $isNew    Is new item
	 * @param   array    $data     The validated data
	 *
	 * @return  boolean
	 *
	 * @since   3.7.0
	 */
	public function onContentAfterSave($context, $item, $isNew, $data =
array())
	{
		// Check if data is an array and the item has an id
		if (!is_array($data) || empty($item->id) ||
empty($data['com_fields']))
		{
			return true;
		}

		// Create correct context for category
		if ($context == 'com_categories.category')
		{
			$context = $item->extension . '.categories';

			// Set the catid on the category to get only the fields which belong to
this category
			$item->catid = $item->id;
		}

		// Check the context
		$parts = FieldsHelper::extract($context, $item);

		if (!$parts)
		{
			return true;
		}

		// Compile the right context for the fields
		$context = $parts[0] . '.' . $parts[1];

		// Loading the fields
		$fields = FieldsHelper::getFields($context, $item);

		if (!$fields)
		{
			return true;
		}

		// Loading the model
		$model = JModelLegacy::getInstance('Field',
'FieldsModel', array('ignore_request' => true));

		// Loop over the fields
		foreach ($fields as $field)
		{
			// Determine the value if it is (un)available from the data
			if (key_exists($field->name, $data['com_fields']))
			{
				$value = $data['com_fields'][$field->name] === false ?
null : $data['com_fields'][$field->name];
			}
			// Field not available on form, use stored value
			else
			{
				$value = $field->rawvalue;
			}

			// If no value set (empty) remove value from database
			if (is_array($value) ? !count($value) : !strlen($value))
			{
				$value = null;
			}

			// JSON encode value for complex fields
			if (is_array($value) && (count($value, COUNT_NORMAL) !==
count($value, COUNT_RECURSIVE) || !count(array_filter(array_keys($value),
'is_numeric'))))
			{
				$value = json_encode($value);
			}

			// Setting the value for the field and the item
			$model->setFieldValue($field->id, $item->id, $value);
		}

		return true;
	}

	/**
	 * The save event.
	 *
	 * @param   array    $userData  The date
	 * @param   boolean  $isNew     Is new
	 * @param   boolean  $success   Is success
	 * @param   string   $msg       The message
	 *
	 * @return  boolean
	 *
	 * @since   3.7.0
	 */
	public function onUserAfterSave($userData, $isNew, $success, $msg)
	{
		// It is not possible to manipulate the user during save events
		// Check if data is valid or we are in a recursion
		if (!$userData['id'] || !$success)
		{
			return true;
		}

		$user = JFactory::getUser($userData['id']);

		$task =
JFactory::getApplication()->input->getCmd('task');

		// Skip fields save when we activate a user, because we will lose the
saved data
		if (in_array($task, array('activate', 'block',
'unblock')))
		{
			return true;
		}

		// Trigger the events with a real user
		$this->onContentAfterSave('com_users.user', $user, false,
$userData);

		return true;
	}

	/**
	 * The delete event.
	 *
	 * @param   string    $context  The context
	 * @param   stdClass  $item     The item
	 *
	 * @return  boolean
	 *
	 * @since   3.7.0
	 */
	public function onContentAfterDelete($context, $item)
	{
		$parts = FieldsHelper::extract($context, $item);

		if (!$parts || empty($item->id))
		{
			return true;
		}

		$context = $parts[0] . '.' . $parts[1];

		JModelLegacy::addIncludePath(JPATH_ADMINISTRATOR .
'/components/com_fields/models', 'FieldsModel');

		$model = JModelLegacy::getInstance('Field',
'FieldsModel', array('ignore_request' => true));
		$model->cleanupValues($context, $item->id);

		return true;
	}

	/**
	 * The user delete event.
	 *
	 * @param   stdClass  $user    The context
	 * @param   boolean   $succes  Is success
	 * @param   string    $msg     The message
	 *
	 * @return  boolean
	 *
	 * @since   3.7.0
	 */
	public function onUserAfterDelete($user, $succes, $msg)
	{
		$item     = new stdClass;
		$item->id = $user['id'];

		return $this->onContentAfterDelete('com_users.user', $item);
	}

	/**
	 * The form event.
	 *
	 * @param   JForm     $form  The form
	 * @param   stdClass  $data  The data
	 *
	 * @return  boolean
	 *
	 * @since   3.7.0
	 */
	public function onContentPrepareForm(JForm $form, $data)
	{
		$context = $form->getName();

		// When a category is edited, the context is
com_categories.categorycom_content
		if (strpos($context, 'com_categories.category') === 0)
		{
			$context = str_replace('com_categories.category',
'', $context) . '.categories';

			// Set the catid on the category to get only the fields which belong to
this category
			if (is_array($data) && key_exists('id', $data))
			{
				$data['catid'] = $data['id'];
			}

			if (is_object($data) && isset($data->id))
			{
				$data->catid = $data->id;
			}
		}

		$parts = FieldsHelper::extract($context, $form);

		if (!$parts)
		{
			return true;
		}

		$input = JFactory::getApplication()->input;

		// If we are on the save command we need the actual data
		$jformData = $input->get('jform', array(),
'array');

		if ($jformData && !$data)
		{
			$data = $jformData;
		}

		if (is_array($data))
		{
			$data = (object) $data;
		}

		FieldsHelper::prepareForm($parts[0] . '.' . $parts[1], $form,
$data);

		return true;
	}

	/**
	 * The display event.
	 *
	 * @param   string    $context     The context
	 * @param   stdClass  $item        The item
	 * @param   Registry  $params      The params
	 * @param   integer   $limitstart  The start
	 *
	 * @return  string
	 *
	 * @since   3.7.0
	 */
	public function onContentAfterTitle($context, $item, $params, $limitstart
= 0)
	{
		return $this->display($context, $item, $params, 1);
	}

	/**
	 * The display event.
	 *
	 * @param   string    $context     The context
	 * @param   stdClass  $item        The item
	 * @param   Registry  $params      The params
	 * @param   integer   $limitstart  The start
	 *
	 * @return  string
	 *
	 * @since   3.7.0
	 */
	public function onContentBeforeDisplay($context, $item, $params,
$limitstart = 0)
	{
		return $this->display($context, $item, $params, 2);
	}

	/**
	 * The display event.
	 *
	 * @param   string    $context     The context
	 * @param   stdClass  $item        The item
	 * @param   Registry  $params      The params
	 * @param   integer   $limitstart  The start
	 *
	 * @return  string
	 *
	 * @since   3.7.0
	 */
	public function onContentAfterDisplay($context, $item, $params,
$limitstart = 0)
	{
		return $this->display($context, $item, $params, 3);
	}

	/**
	 * Performs the display event.
	 *
	 * @param   string    $context      The context
	 * @param   stdClass  $item         The item
	 * @param   Registry  $params       The params
	 * @param   integer   $displayType  The type
	 *
	 * @return  string
	 *
	 * @since   3.7.0
	 */
	private function display($context, $item, $params, $displayType)
	{
		$parts = FieldsHelper::extract($context, $item);

		if (!$parts)
		{
			return '';
		}

		// If we have a category, set the catid field to fetch only the fields
which belong to it
		if ($parts[1] == 'categories' &&
!isset($item->catid))
		{
			$item->catid = $item->id;
		}

		$context = $parts[0] . '.' . $parts[1];

		// Convert tags
		if ($context == 'com_tags.tag' &&
!empty($item->type_alias))
		{
			// Set the context
			$context = $item->type_alias;

			$item = $this->prepareTagItem($item);
		}

		if (is_string($params) || !$params)
		{
			$params = new Registry($params);
		}

		$fields = FieldsHelper::getFields($context, $item, $displayType);

		if ($fields)
		{
			$app = Factory::getApplication();

			if ($app->isClient('site') &&
Multilanguage::isEnabled() && isset($item->language) &&
$item->language == '*')
			{
				$lang = $app->getLanguage()->getTag();

				foreach ($fields as $key => $field)
				{
					if ($field->language == '*' || $field->language ==
$lang)
					{
						continue;
					}

					unset($fields[$key]);
				}
			}
		}

		if ($fields)
		{
			foreach ($fields as $key => $field)
			{
				$fieldDisplayType = $field->params->get('display',
'2');

				if ($fieldDisplayType == $displayType)
				{
					continue;
				}

				unset($fields[$key]);
			}
		}

		if ($fields)
		{
			return FieldsHelper::render(
				$context,
				'fields.render',
				array(
					'item'            => $item,
					'context'         => $context,
					'fields'          => $fields
				)
			);
		}

		return '';
	}

	/**
	 * Performs the display event.
	 *
	 * @param   string    $context  The context
	 * @param   stdClass  $item     The item
	 *
	 * @return  void
	 *
	 * @since   3.7.0
	 */
	public function onContentPrepare($context, $item)
	{
		// Check property exists (avoid costly & useless recreation), if need
to recreate them, just unset the property!
		if (isset($item->jcfields))
		{
			return;
		}

		$parts = FieldsHelper::extract($context, $item);

		if (!$parts)
		{
			return;
		}

		$context = $parts[0] . '.' . $parts[1];

		// Convert tags
		if ($context == 'com_tags.tag' &&
!empty($item->type_alias))
		{
			// Set the context
			$context = $item->type_alias;

			$item = $this->prepareTagItem($item);
		}

		// Get item's fields, also preparing their value property for manual
display
		// (calling plugins events and loading layouts to get their HTML display)
		$fields = FieldsHelper::getFields($context, $item, true);

		// Adding the fields to the object
		$item->jcfields = array();

		foreach ($fields as $key => $field)
		{
			$item->jcfields[$field->id] = $field;
		}
	}

	/**
	 * The finder event.
	 *
	 * @param   stdClass  $item  The item
	 *
	 * @return  boolean
	 *
	 * @since   3.7.0
	 */
	public function onPrepareFinderContent($item)
	{
		$section = strtolower($item->layout);
		$tax     = $item->getTaxonomy('Type');

		if ($tax)
		{
			foreach ($tax as $context => $value)
			{
				// This is only a guess, needs to be improved
				$component = strtolower($context);

				if (strpos($context, 'com_') !== 0)
				{
					$component = 'com_' . $component;
				}

				// Transform com_article to com_content
				if ($component === 'com_article')
				{
					$component = 'com_content';
				}

				// Create a dummy object with the required fields
				$tmp     = new stdClass;
				$tmp->id = $item->__get('id');

				if ($item->__get('catid'))
				{
					$tmp->catid = $item->__get('catid');
				}

				// Getting the fields for the constructed context
				$fields = FieldsHelper::getFields($component . '.' .
$section, $tmp, true);

				if (is_array($fields))
				{
					foreach ($fields as $field)
					{
						// Adding the instructions how to handle the text
						$item->addInstruction(FinderIndexer::TEXT_CONTEXT,
$field->name);

						// Adding the field value as a field
						$item->{$field->name} = $field->value;
					}
				}
			}
		}

		return true;
	}

	/**
	 * Prepares a tag item to be ready for com_fields.
	 *
	 * @param   stdClass  $item  The item
	 *
	 * @return  object
	 *
	 * @since   3.8.4
	 */
	private function prepareTagItem($item)
	{
		// Map core fields
		$item->id       = $item->content_item_id;
		$item->language = $item->core_language;

		// Also handle the catid
		if (!empty($item->core_catid))
		{
			$item->catid = $item->core_catid;
		}

		return $item;
	}
}
PK|��[W���fields/fields.xmlnu�[���<?xml
version="1.0" encoding="utf-8" ?>
<extension type="plugin" version="3.7.0"
group="system" method="upgrade">
	<name>plg_system_fields</name>
	<author>Joomla! Project</author>
	<creationDate>March 2016</creationDate>
	<copyright>(C) 2016 Open Source Matters, Inc.</copyright>
	<license>GNU General Public License version 2 or later; see
LICENSE.txt</license>
	<authorEmail>admin@joomla.org</authorEmail>
	<authorUrl>www.joomla.org</authorUrl>
	<version>3.7.0</version>
	<description>PLG_SYSTEM_FIELDS_XML_DESCRIPTION</description>
	<files>
		<filename plugin="fields">fields.php</filename>
	</files>
	<languages>
		<language
tag="en-GB">en-GB.plg_system_fields.ini</language>
		<language
tag="en-GB">en-GB.plg_system_fields.sys.ini</language>
	</languages>
</extension>
PK|��[#�K<<highlight/highlight.phpnu�[���<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.Highlight
 *
 * @copyright   (C) 2011 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * System plugin to highlight terms.
 *
 * @since  2.5
 */
class PlgSystemHighlight extends JPlugin
{
	/**
	 * Method to catch the onAfterDispatch event.
	 *
	 * This is where we setup the click-through content highlighting for.
	 * The highlighting is done with JavaScript so we just
	 * need to check a few parameters and the JHtml behavior will do the rest.
	 *
	 * @return  boolean  True on success
	 *
	 * @since   2.5
	 */
	public function onAfterDispatch()
	{
		// Check that we are in the site application.
		if (JFactory::getApplication()->isClient('administrator'))
		{
			return true;
		}

		// Set the variables.
		$input = JFactory::getApplication()->input;
		$extension = $input->get('option', '',
'cmd');

		// Check if the highlighter is enabled.
		if
(!JComponentHelper::getParams($extension)->get('highlight_terms',
1))
		{
			return true;
		}

		// Check if the highlighter should be activated in this environment.
		if ($input->get('tmpl', '', 'cmd') ===
'component' || JFactory::getDocument()->getType() !==
'html')
		{
			return true;
		}

		// Get the terms to highlight from the request.
		$terms = $input->request->get('highlight', null,
'base64');
		$terms = $terms ? json_decode(base64_decode($terms)) : null;

		// Check the terms.
		if (empty($terms))
		{
			return true;
		}

		// Clean the terms array.
		$filter     = JFilterInput::getInstance();

		$cleanTerms = array();

		foreach ($terms as $term)
		{
			$cleanTerms[] = htmlspecialchars($filter->clean($term,
'string'));
		}

		// Activate the highlighter.
		JHtml::_('behavior.highlighter', $cleanTerms);

		// Adjust the component buffer.
		$doc = JFactory::getDocument();
		$buf = $doc->getBuffer('component');
		$buf = '<br id="highlighter-start" />' . $buf .
'<br id="highlighter-end" />';
		$doc->setBuffer($buf, 'component');

		return true;
	}
}
PK|��[;��f44highlight/highlight.xmlnu�[���<?xml
version="1.0" encoding="utf-8"?>
<extension version="3.1" type="plugin"
group="system" method="upgrade">
	<name>plg_system_highlight</name>
	<author>Joomla! Project</author>
	<creationDate>August 2011</creationDate>
	<copyright>(C) 2011 Open Source Matters, Inc.</copyright>
	<license>GNU General Public License version 2 or later;
see	LICENSE.txt</license>
	<authorEmail>admin@joomla.org</authorEmail>
	<authorUrl>www.joomla.org</authorUrl>
	<version>3.0.0</version>
	<description>PLG_SYSTEM_HIGHLIGHT_XML_DESCRIPTION</description>
	<files>
		<filename
plugin="highlight">highlight.php</filename>
	</files>
	<languages>
		<language
tag="en-GB">language/en-GB/en-GB.plg_system_highlight.ini</language>
		<language
tag="en-GB">language/en-GB/en-GB.plg_system_highlight.sys.ini</language>
	</languages>
</extension>
PK|��[�����=languagecode/language/en-GB/en-GB.plg_system_languagecode.ininu�[���;
Joomla! Project
; (C) 2011 Open Source Matters, Inc. <https://www.joomla.org>
; License GNU General Public License version 2 or later; see LICENSE.txt
; Note : All ini files need to be saved as UTF-8

PLG_SYSTEM_LANGUAGECODE="System - Language Code"
PLG_SYSTEM_LANGUAGECODE_FIELD_DESC="Changes the language code used for
the <em>%s</em> language."
PLG_SYSTEM_LANGUAGECODE_FIELDSET_DESC="Changes the language code for
the generated HTML document. Example usage: You have installed the fr-FR
language pack and want the Search Engines to recognise the page as aimed at
French-speaking Canada. Add the tag 'fr-CA' to the corresponding
field for 'fr-FR' to resolve this."
PLG_SYSTEM_LANGUAGECODE_FIELDSET_LABEL="Language codes"
PLG_SYSTEM_LANGUAGECODE_XML_DESCRIPTION="Provides the ability to
change the language code in the generated HTML document to improve
SEO.<br />The fields will appear when the plugin is enabled and
saved."
PK|��[o���Alanguagecode/language/en-GB/en-GB.plg_system_languagecode.sys.ininu�[���;
Joomla! Project
; (C) 2011 Open Source Matters, Inc. <https://www.joomla.org>
; License GNU General Public License version 2 or later; see LICENSE.txt
; Note : All ini files need to be saved as UTF-8

PLG_SYSTEM_LANGUAGECODE="System - Language Code"
PLG_SYSTEM_LANGUAGECODE_XML_DESCRIPTION="Provides ability to change
the language code in the generated HTML document to improve SEO"

PK|��[�}`languagecode/languagecode.phpnu�[���<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.languagecode
 *
 * @copyright   (C) 2011 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Language Code plugin class.
 *
 * @since  2.5
 */
class PlgSystemLanguagecode extends JPlugin
{
	/**
	 * Plugin that changes the language code used in the <html /> tag.
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	public function onAfterRender()
	{
		$app = JFactory::getApplication();

		// Use this plugin only in site application.
		if ($app->isClient('site'))
		{
			// Get the response body.
			$body = $app->getBody();

			// Get the current language code.
			$code = JFactory::getDocument()->getLanguage();

			// Get the new code.
			$new_code  = $this->params->get($code);

			// Replace the old code by the new code in the <html /> tag.
			if ($new_code)
			{
				// Replace the new code in the HTML document.
				$patterns = array(
					chr(1) . '(<html.*\s+xml:lang=")(' . $code .
')(".*>)' . chr(1) . 'i',
					chr(1) . '(<html.*\s+lang=")(' . $code .
')(".*>)' . chr(1) . 'i',
				);
				$replace = array(
					'${1}' . strtolower($new_code) . '${3}',
					'${1}' . strtolower($new_code) . '${3}'
				);
			}
			else
			{
				$patterns = array();
				$replace  = array();
			}

			// Replace codes in <link hreflang="" /> attributes.
			preg_match_all(chr(1) .
'(<link.*\s+hreflang=")([0-9a-z\-]*)(".*\s+rel="alternate".*/>)'
. chr(1) . 'i', $body, $matches);

			foreach ($matches[2] as $match)
			{
				$new_code = $this->params->get(strtolower($match));

				if ($new_code)
				{
					$patterns[] = chr(1) . '(<link.*\s+hreflang=")(' .
$match . ')(".*\s+rel="alternate".*/>)' .
chr(1) . 'i';
					$replace[] = '${1}' . $new_code . '${3}';
				}
			}

			preg_match_all(chr(1) .
'(<link.*\s+rel="alternate".*\s+hreflang=")([0-9A-Za-z\-]*)(".*/>)'
. chr(1) . 'i', $body, $matches);

			foreach ($matches[2] as $match)
			{
				$new_code = $this->params->get(strtolower($match));

				if ($new_code)
				{
					$patterns[] = chr(1) .
'(<link.*\s+rel="alternate".*\s+hreflang=")(' .
$match . ')(".*/>)' . chr(1) . 'i';
					$replace[] = '${1}' . $new_code . '${3}';
				}
			}

			// Replace codes in itemprop content
			preg_match_all(chr(1) .
'(<meta.*\s+itemprop="inLanguage".*\s+content=")([0-9A-Za-z\-]*)(".*/>)'
. chr(1) . 'i', $body, $matches);

			foreach ($matches[2] as $match)
			{
				$new_code = $this->params->get(strtolower($match));

				if ($new_code)
				{
					$patterns[] = chr(1) .
'(<meta.*\s+itemprop="inLanguage".*\s+content=")('
. $match . ')(".*/>)' . chr(1) . 'i';
					$replace[] = '${1}' . $new_code . '${3}';
				}
			}

			$app->setBody(preg_replace($patterns, $replace, $body));
		}
	}

	/**
	 * Prepare form.
	 *
	 * @param   JForm  $form  The form to be altered.
	 * @param   mixed  $data  The associated data for the form.
	 *
	 * @return  boolean
	 *
	 * @since	2.5
	 */
	public function onContentPrepareForm(JForm $form, $data)
	{
		// Check we are manipulating the languagecode plugin.
		if ($form->getName() !== 'com_plugins.plugin' ||
!$form->getField('languagecodeplugin', 'params'))
		{
			return true;
		}

		// Get site languages.
		if ($languages = JLanguageHelper::getKnownLanguages(JPATH_SITE))
		{
			// Inject fields into the form.
			foreach ($languages as $tag => $language)
			{
				$form->load('
					<form>
						<fields name="params">
							<fieldset
								name="languagecode"
								label="PLG_SYSTEM_LANGUAGECODE_FIELDSET_LABEL"
								description="PLG_SYSTEM_LANGUAGECODE_FIELDSET_DESC"
							>
								<field
									name="' . strtolower($tag) . '"
									type="text"
									label="' . $tag . '"
									description="' .
htmlspecialchars(JText::sprintf('PLG_SYSTEM_LANGUAGECODE_FIELD_DESC',
$language['name']), ENT_COMPAT, 'UTF-8') . '"
									translate_description="false"
									translate_label="false"
									size="7"
									filter="cmd"
								/>
							</fieldset>
						</fields>
					</form>
				');
			}
		}

		return true;
	}
}
PK|��[�x�.��languagecode/languagecode.xmlnu�[���<?xml
version="1.0" encoding="utf-8"?>
<extension version="3.1" type="plugin"
group="system" method="upgrade">
	<name>plg_system_languagecode</name>
	<author>Joomla! Project</author>
	<creationDate>November 2011</creationDate>
	<copyright>(C) 2011 Open Source Matters, Inc.</copyright>
	<license>GNU General Public License version 2 or later; see
LICENSE.txt</license>
	<authorEmail>admin@joomla.org</authorEmail>
	<authorUrl>www.joomla.org</authorUrl>
	<version>3.0.0</version>
	<description>PLG_SYSTEM_LANGUAGECODE_XML_DESCRIPTION</description>
	<files>
		<filename
plugin="languagecode">languagecode.php</filename>
		<folder>language</folder>
	</files>
	<languages>
		<language
tag="en-GB">language/en-GB/en-GB.plg_system_languagecode.ini</language>
		<language
tag="en-GB">language/en-GB/en-GB.plg_system_languagecode.sys.ini</language>
	</languages>
	<config>
		<fields name="params">
			<field
				name="languagecodeplugin"
				type="hidden"
				default="true"
			/>
		</fields>
	</config>
</extension>
PK|��[�ա�b�b!languagefilter/languagefilter.phpnu�[���<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.languagefilter
 *
 * @copyright   (C) 2010 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\Registry\Registry;
use Joomla\String\StringHelper;

JLoader::register('MenusHelper', JPATH_ADMINISTRATOR .
'/components/com_menus/helpers/menus.php');

/**
 * Joomla! Language Filter Plugin.
 *
 * @since  1.6
 */
class PlgSystemLanguageFilter extends JPlugin
{
	/**
	 * The routing mode.
	 *
	 * @var    boolean
	 * @since  2.5
	 */
	protected $mode_sef;

	/**
	 * Available languages by sef.
	 *
	 * @var    array
	 * @since  1.6
	 */
	protected $sefs;

	/**
	 * Available languages by language codes.
	 *
	 * @var    array
	 * @since  2.5
	 */
	protected $lang_codes;

	/**
	 * The current language code.
	 *
	 * @var    string
	 * @since  3.4.2
	 */
	protected $current_lang;

	/**
	 * The default language code.
	 *
	 * @var    string
	 * @since  2.5
	 */
	protected $default_lang;

	/**
	 * The logged user language code.
	 *
	 * @var    string
	 * @since  3.3.1
	 */
	private $user_lang_code;

	/**
	 * Application object.
	 *
	 * @var    JApplicationCms
	 * @since  3.3
	 */
	protected $app;

	/**
	 * Constructor.
	 *
	 * @param   object  &$subject  The object to observe
	 * @param   array   $config    An optional associative array of
configuration settings.
	 *
	 * @since   1.6
	 */
	public function __construct(&$subject, $config)
	{
		parent::__construct($subject, $config);

		$this->app = JFactory::getApplication();

		// Setup language data.
		$this->mode_sef     = $this->app->get('sef', 0);
		$this->sefs         = JLanguageHelper::getLanguages('sef');
		$this->lang_codes   =
JLanguageHelper::getLanguages('lang_code');
		$this->default_lang =
JComponentHelper::getParams('com_languages')->get('site',
'en-GB');

		// If language filter plugin is executed in a site page.
		if ($this->app->isClient('site'))
		{
			$levels = JFactory::getUser()->getAuthorisedViewLevels();

			foreach ($this->sefs as $sef => $language)
			{
				// @todo: In Joomla 2.5.4 and earlier access wasn't set. Non
modified Content Languages got 0 as access value
				// we also check if frontend language exists and is enabled
				if (($language->access && !in_array($language->access,
$levels))
					|| (!array_key_exists($language->lang_code,
JLanguageHelper::getInstalledLanguages(0))))
				{
					unset($this->lang_codes[$language->lang_code],
$this->sefs[$language->sef]);
				}
			}
		}
		// If language filter plugin is executed in an admin page (ex: JRoute
site).
		else
		{
			// Set current language to default site language, fallback to en-GB if
there is no content language for the default site language.
			$this->current_lang =
isset($this->lang_codes[$this->default_lang]) ?
$this->default_lang : 'en-GB';

			foreach ($this->sefs as $sef => $language)
			{
				if (!array_key_exists($language->lang_code,
JLanguageHelper::getInstalledLanguages(0)))
				{
					unset($this->lang_codes[$language->lang_code]);
					unset($this->sefs[$language->sef]);
				}
			}
		}
	}

	/**
	 * After initialise.
	 *
	 * @return  void
	 *
	 * @since   1.6
	 */
	public function onAfterInitialise()
	{
		$this->app->item_associations =
$this->params->get('item_associations', 0);

		// We need to make sure we are always using the site router, even if the
language plugin is executed in admin app.
		$router =
JApplicationCms::getInstance('site')->getRouter('site');

		// Attach build rules for language SEF.
		$router->attachBuildRule(array($this,
'preprocessBuildRule'), JRouter::PROCESS_BEFORE);
		$router->attachBuildRule(array($this, 'buildRule'),
JRouter::PROCESS_DURING);

		if ($this->mode_sef)
		{
			$router->attachBuildRule(array($this,
'postprocessSEFBuildRule'), JRouter::PROCESS_AFTER);
		}
		else
		{
			$router->attachBuildRule(array($this,
'postprocessNonSEFBuildRule'), JRouter::PROCESS_AFTER);
		}

		// Attach parse rules for language SEF.
		$router->attachParseRule(array($this, 'parseRule'),
JRouter::PROCESS_DURING);
	}

	/**
	 * After route.
	 *
	 * @return  void
	 *
	 * @since   3.4
	 */
	public function onAfterRoute()
	{
		// Add custom site name.
		if ($this->app->isClient('site') &&
isset($this->lang_codes[$this->current_lang]) &&
$this->lang_codes[$this->current_lang]->sitename)
		{
			$this->app->set('sitename',
$this->lang_codes[$this->current_lang]->sitename);
		}
	}

	/**
	 * Add build preprocess rule to router.
	 *
	 * @param   JRouter  &$router  JRouter object.
	 * @param   JUri     &$uri     JUri object.
	 *
	 * @return  void
	 *
	 * @since   3.4
	 */
	public function preprocessBuildRule(&$router, &$uri)
	{
		$lang = $uri->getVar('lang', $this->current_lang);
		$uri->setVar('lang', $lang);

		if (isset($this->sefs[$lang]))
		{
			$lang = $this->sefs[$lang]->lang_code;
			$uri->setVar('lang', $lang);
		}
	}

	/**
	 * Add build rule to router.
	 *
	 * @param   JRouter  &$router  JRouter object.
	 * @param   JUri     &$uri     JUri object.
	 *
	 * @return  void
	 *
	 * @since   1.6
	 */
	public function buildRule(&$router, &$uri)
	{
		$lang = $uri->getVar('lang');

		if (isset($this->lang_codes[$lang]))
		{
			$sef = $this->lang_codes[$lang]->sef;
		}
		else
		{
			$sef = $this->lang_codes[$this->current_lang]->sef;
		}

		if ($this->mode_sef
			&& (!$this->params->get('remove_default_prefix',
0)
			|| $lang !== $this->default_lang
			|| $lang !== $this->current_lang))
		{
			$uri->setPath($uri->getPath() . '/' . $sef .
'/');
		}
	}

	/**
	 * postprocess build rule for SEF URLs
	 *
	 * @param   JRouter  &$router  JRouter object.
	 * @param   JUri     &$uri     JUri object.
	 *
	 * @return  void
	 *
	 * @since   3.4
	 */
	public function postprocessSEFBuildRule(&$router, &$uri)
	{
		$uri->delVar('lang');
	}

	/**
	 * postprocess build rule for non-SEF URLs
	 *
	 * @param   JRouter  &$router  JRouter object.
	 * @param   JUri     &$uri     JUri object.
	 *
	 * @return  void
	 *
	 * @since   3.4
	 */
	public function postprocessNonSEFBuildRule(&$router, &$uri)
	{
		$lang = $uri->getVar('lang');

		if (isset($this->lang_codes[$lang]))
		{
			$uri->setVar('lang', $this->lang_codes[$lang]->sef);
		}
	}

	/**
	 * Add parse rule to router.
	 *
	 * @param   JRouter  &$router  JRouter object.
	 * @param   JUri     &$uri     JUri object.
	 *
	 * @return  void
	 *
	 * @since   1.6
	 */
	public function parseRule(&$router, &$uri)
	{
		// Did we find the current and existing language yet?
		$found = false;

		// Are we in SEF mode or not?
		if ($this->mode_sef)
		{
			$path = $uri->getPath();
			$parts = explode('/', $path);

			$sef = StringHelper::strtolower($parts[0]);

			// Do we have a URL Language Code ?
			if (!isset($this->sefs[$sef]))
			{
				// Check if remove default URL language code is set
				if ($this->params->get('remove_default_prefix', 0))
				{
					if ($parts[0])
					{
						// We load a default site language page
						$lang_code = $this->default_lang;
					}
					else
					{
						// We check for an existing language cookie
						$lang_code = $this->getLanguageCookie();
					}
				}
				else
				{
					$lang_code = $this->getLanguageCookie();
				}

				// No language code. Try using browser settings or default site
language
				if (!$lang_code &&
$this->params->get('detect_browser', 0) == 1)
				{
					$lang_code = JLanguageHelper::detectLanguage();
				}

				if (!$lang_code)
				{
					$lang_code = $this->default_lang;
				}

				if ($lang_code === $this->default_lang &&
$this->params->get('remove_default_prefix', 0))
				{
					$found = true;
				}
			}
			else
			{
				// We found our language
				$found = true;
				$lang_code = $this->sefs[$sef]->lang_code;

				// If we found our language, but its the default language and we
don't want a prefix for that, we are on a wrong URL.
				// Or we try to change the language back to the default language. We
need a redirect to the proper URL for the default language.
				if ($lang_code === $this->default_lang &&
$this->params->get('remove_default_prefix', 0))
				{
					// Create a cookie.
					$this->setLanguageCookie($lang_code);

					$found = false;
					array_shift($parts);
					$path = implode('/', $parts);
				}

				// We have found our language and the first part of our URL is the
language prefix
				if ($found)
				{
					array_shift($parts);

					// Empty parts array when "index.php" is the only part left.
					if (count($parts) === 1 && $parts[0] ===
'index.php')
					{
						$parts = array();
					}

					$uri->setPath(implode('/', $parts));
				}
			}
		}
		// We are not in SEF mode
		else
		{
			$lang_code = $this->getLanguageCookie();

			if (!$lang_code &&
$this->params->get('detect_browser', 1))
			{
				$lang_code = JLanguageHelper::detectLanguage();
			}

			if (!isset($this->lang_codes[$lang_code]))
			{
				$lang_code = $this->default_lang;
			}
		}

		$lang = $uri->getVar('lang', $lang_code);

		if (isset($this->sefs[$lang]))
		{
			// We found our language
			$found = true;
			$lang_code = $this->sefs[$lang]->lang_code;
		}

		// We are called via POST or the nolangfilter url parameter was set. We
don't care about the language
		// and simply set the default language as our current language.
		if ($this->app->input->getMethod() === 'POST'
			|| $this->app->input->get('nolangfilter', 0) == 1
			|| count($this->app->input->post) > 0
			|| count($this->app->input->files) > 0)
		{
			$found = true;

			if (!isset($lang_code))
			{
				$lang_code = $this->getLanguageCookie();
			}

			if (!$lang_code &&
$this->params->get('detect_browser', 1))
			{
				$lang_code = JLanguageHelper::detectLanguage();
			}

			if (!isset($this->lang_codes[$lang_code]))
			{
				$lang_code = $this->default_lang;
			}
		}

		// We have not found the language and thus need to redirect
		if (!$found)
		{
			// Lets find the default language for this user
			if (!isset($lang_code) || !isset($this->lang_codes[$lang_code]))
			{
				$lang_code = false;

				if ($this->params->get('detect_browser', 1))
				{
					$lang_code = JLanguageHelper::detectLanguage();

					if (!isset($this->lang_codes[$lang_code]))
					{
						$lang_code = false;
					}
				}

				if (!$lang_code)
				{
					$lang_code = $this->default_lang;
				}
			}

			if ($this->mode_sef)
			{
				// Use the current language sef or the default one.
				if ($lang_code !== $this->default_lang
					|| !$this->params->get('remove_default_prefix', 0))
				{
					$path = $this->lang_codes[$lang_code]->sef . '/' .
$path;
				}

				$uri->setPath($path);

				if (!$this->app->get('sef_rewrite'))
				{
					$uri->setPath('index.php/' . $uri->getPath());
				}

				$redirectUri = $uri->base() .
$uri->toString(array('path', 'query',
'fragment'));
			}
			else
			{
				$uri->setVar('lang',
$this->lang_codes[$lang_code]->sef);
				$redirectUri = $uri->base() . 'index.php?' .
$uri->getQuery();
			}

			// Set redirect HTTP code to "302 Found".
			$redirectHttpCode = 302;

			// If selected language is the default language redirect code is
"301 Moved Permanently".
			if ($lang_code === $this->default_lang)
			{
				$redirectHttpCode = 301;

				// We cannot cache this redirect in browser. 301 is cachable by default
so we need to force to not cache it in browsers.
				$this->app->setHeader('Expires', 'Wed, 17 Aug 2005
00:00:00 GMT', true);
				$this->app->setHeader('Last-Modified', gmdate('D,
d M Y H:i:s') . ' GMT', true);
				$this->app->setHeader('Cache-Control', 'no-store,
no-cache, must-revalidate, post-check=0, pre-check=0', false);
				$this->app->setHeader('Pragma', 'no-cache');
				$this->app->sendHeaders();
			}

			// Redirect to language.
			$this->app->redirect($redirectUri, $redirectHttpCode);
		}

		// We have found our language and now need to set the cookie and the
language value in our system
		$array = array('lang' => $lang_code);
		$this->current_lang = $lang_code;

		// Set the request var.
		$this->app->input->set('language', $lang_code);
		$this->app->set('language', $lang_code);
		$language = JFactory::getLanguage();

		if ($language->getTag() !== $lang_code)
		{
			$language_new = JLanguage::getInstance($lang_code, (bool)
$this->app->get('debug_lang'));

			foreach ($language->getPaths() as $extension => $files)
			{
				if (strpos($extension, 'plg_system') !== false)
				{
					$extension_name = substr($extension, 11);

					$language_new->load($extension, JPATH_ADMINISTRATOR)
					|| $language_new->load($extension, JPATH_PLUGINS .
'/system/' . $extension_name);

					continue;
				}

				$language_new->load($extension);
			}

			JFactory::$language = $language_new;
			$this->app->loadLanguage($language_new);
		}

		// Create a cookie.
		if ($this->getLanguageCookie() !== $lang_code)
		{
			$this->setLanguageCookie($lang_code);
		}

		return $array;
	}

	/**
	 * Reports the privacy related capabilities for this plugin to site
administrators.
	 *
	 * @return  array
	 *
	 * @since   3.9.0
	 */
	public function onPrivacyCollectAdminCapabilities()
	{
		$this->loadLanguage();

		return array(
			JText::_('PLG_SYSTEM_LANGUAGEFILTER') => array(
				JText::_('PLG_SYSTEM_LANGUAGEFILTER_PRIVACY_CAPABILITY_LANGUAGE_COOKIE'),
			)
		);
	}

	/**
	 * Before store user method.
	 *
	 * Method is called before user data is stored in the database.
	 *
	 * @param   array    $user   Holds the old user data.
	 * @param   boolean  $isnew  True if a new user is stored.
	 * @param   array    $new    Holds the new user data.
	 *
	 * @return  void
	 *
	 * @since   1.6
	 */
	public function onUserBeforeSave($user, $isnew, $new)
	{
		if (array_key_exists('params', $user) &&
$this->params->get('automatic_change', 1) == 1)
		{
			$registry = new Registry($user['params']);
			$this->user_lang_code = $registry->get('language');

			if (empty($this->user_lang_code))
			{
				$this->user_lang_code = $this->current_lang;
			}
		}
	}

	/**
	 * After store user method.
	 *
	 * Method is called after user data is stored in the database.
	 *
	 * @param   array    $user     Holds the new user data.
	 * @param   boolean  $isnew    True if a new user is stored.
	 * @param   boolean  $success  True if user was succesfully stored in the
database.
	 * @param   string   $msg      Message.
	 *
	 * @return  void
	 *
	 * @since   1.6
	 */
	public function onUserAfterSave($user, $isnew, $success, $msg)
	{
		if ($success && array_key_exists('params', $user)
&& $this->params->get('automatic_change', 1) == 1)
		{
			$registry = new Registry($user['params']);
			$lang_code = $registry->get('language');

			if (empty($lang_code))
			{
				$lang_code = $this->current_lang;
			}

			if ($lang_code === $this->user_lang_code ||
!isset($this->lang_codes[$lang_code]))
			{
				if ($this->app->isClient('site'))
				{
					$this->app->setUserState('com_users.edit.profile.redirect',
null);
				}
			}
			else
			{
				if ($this->app->isClient('site'))
				{
					$this->app->setUserState('com_users.edit.profile.redirect',
'index.php?Itemid='
						. $this->app->getMenu()->getDefault($lang_code)->id .
'&lang=' . $this->lang_codes[$lang_code]->sef
					);

					// Create a cookie.
					$this->setLanguageCookie($lang_code);
				}
			}
		}
	}

	/**
	 * Method to handle any login logic and report back to the subject.
	 *
	 * @param   array  $user     Holds the user data.
	 * @param   array  $options  Array holding options (remember,
autoregister, group).
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   1.5
	 */
	public function onUserLogin($user, $options = array())
	{
		$menu = $this->app->getMenu();

		if ($this->app->isClient('site'))
		{
			if ($this->params->get('automatic_change', 1))
			{
				$assoc = JLanguageAssociations::isEnabled();
				$lang_code = $user['language'];

				// If no language is specified for this user, we set it to the site
default language
				if (empty($lang_code))
				{
					$lang_code = $this->default_lang;
				}

				jimport('joomla.filesystem.folder');

				// The language has been deleted/disabled or the related content
language does not exist/has been unpublished
				// or the related home page does not exist/has been unpublished
				if (!array_key_exists($lang_code, $this->lang_codes)
					|| !array_key_exists($lang_code,
JLanguageMultilang::getSiteHomePages())
					|| !JFolder::exists(JPATH_SITE . '/language/' . $lang_code))
				{
					$lang_code = $this->current_lang;
				}

				// Try to get association from the current active menu item
				$active = $menu->getActive();

				$foundAssociation = false;

				/**
				 * Looking for associations.
				 * If the login menu item form contains an internal URL redirection,
				 * This will override the automatic change to the user preferred site
language.
				 * In that case we use the redirect as defined in the menu item.
				 *  Otherwise we redirect, when available, to the user preferred site
language.
				 */
				if ($active &&
!$active->params['login_redirect_url'])
				{
					if ($assoc)
					{
						$associations = MenusHelper::getAssociations($active->id);
					}

					// Retrieves the Itemid from a login form.
					$uri = new
JUri($this->app->getUserState('users.login.form.return'));

					if ($uri->getVar('Itemid'))
					{
						// The login form contains a menu item redirection. Try to get
associations from that menu item.
						// If any association set to the user preferred site language,
redirect to that page.
						if ($assoc)
						{
							$associations =
MenusHelper::getAssociations($uri->getVar('Itemid'));
						}

						if (isset($associations[$lang_code]) &&
$menu->getItem($associations[$lang_code]))
						{
							$associationItemid = $associations[$lang_code];
							$this->app->setUserState('users.login.form.return',
'index.php?Itemid=' . $associationItemid);
							$foundAssociation = true;
						}
					}
					elseif (isset($associations[$lang_code]) &&
$menu->getItem($associations[$lang_code]))
					{
						/**
						 * The login form does not contain a menu item redirection.
						 * The active menu item has associations.
						 * We redirect to the user preferred site language associated page.
						 */
						$associationItemid = $associations[$lang_code];
						$this->app->setUserState('users.login.form.return',
'index.php?Itemid=' . $associationItemid);
						$foundAssociation = true;
					}
					elseif ($active->home)
					{
						// We are on a Home page, we redirect to the user preferred site
language Home page.
						$item = $menu->getDefault($lang_code);

						if ($item && $item->language !== $active->language
&& $item->language !== '*')
						{
							$this->app->setUserState('users.login.form.return',
'index.php?Itemid=' . $item->id);
							$foundAssociation = true;
						}
					}
				}

				if ($foundAssociation && $lang_code !== $this->current_lang)
				{
					// Change language.
					$this->current_lang = $lang_code;

					// Create a cookie.
					$this->setLanguageCookie($lang_code);

					// Change the language code.
					JFactory::getLanguage()->setLanguage($lang_code);
				}
			}
			else
			{
				if
($this->app->getUserState('users.login.form.return'))
				{
					$this->app->setUserState('users.login.form.return',
JRoute::_($this->app->getUserState('users.login.form.return'),
false));
				}
			}
		}
	}

	/**
	 * Method to add alternative meta tags for associated menu items.
	 *
	 * @return  void
	 *
	 * @since   1.7
	 */
	public function onAfterDispatch()
	{
		$doc = JFactory::getDocument();

		if ($this->app->isClient('site') &&
$this->params->get('alternate_meta', 1) &&
$doc->getType() === 'html')
		{
			$languages             = $this->lang_codes;
			$homes                 = JLanguageMultilang::getSiteHomePages();
			$menu                  = $this->app->getMenu();
			$active                = $menu->getActive();
			$levels                =
JFactory::getUser()->getAuthorisedViewLevels();
			$remove_default_prefix =
$this->params->get('remove_default_prefix', 0);
			$server                =
JUri::getInstance()->toString(array('scheme',
'host', 'port'));
			$is_home               = false;
			$currentInternalUrl    = 'index.php?' .
http_build_query($this->app->getRouter()->getVars());

			if ($active)
			{
				$active_link  = JRoute::_($active->link . '&Itemid=' .
$active->id);
				$current_link = JRoute::_($currentInternalUrl);

				// Load menu associations
				if ($active_link === $current_link)
				{
					$associations = MenusHelper::getAssociations($active->id);
				}

				// Check if we are on the home page
				$is_home = ($active->home
					&& ($active_link === $current_link || $active_link ===
$current_link . 'index.php' || $active_link . '/' ===
$current_link));
			}

			// Load component associations.
			$option = $this->app->input->get('option');
			$cName = ucfirst(substr($option, 4)) . 'HelperAssociation';
			JLoader::register($cName, JPath::clean(JPATH_SITE .
'/components/' . $option .
'/helpers/association.php'));

			if (class_exists($cName) && is_callable(array($cName,
'getAssociations')))
			{
				$cassociations = call_user_func(array($cName,
'getAssociations'));
			}

			// For each language...
			foreach ($languages as $i => $language)
			{
				switch (true)
				{
					// Language without frontend UI || Language without specific home menu
|| Language without authorized access level
					case (!array_key_exists($i,
JLanguageHelper::getInstalledLanguages(0))):
					case (!isset($homes[$i])):
					case (isset($language->access) && $language->access
&& !in_array($language->access, $levels)):
						unset($languages[$i]);
						break;

					// Home page
					case ($is_home):
						$language->link = JRoute::_('index.php?lang=' .
$language->sef . '&Itemid=' . $homes[$i]->id);
						break;

					// Current language link
					case ($i === $this->current_lang):
						$language->link = JRoute::_($currentInternalUrl);
						break;

					// Component association
					case (isset($cassociations[$i])):
						$language->link = JRoute::_($cassociations[$i] .
'&lang=' . $language->sef);
						break;

					// Menu items association
					// Heads up! "$item = $menu" here below is an assignment,
*NOT* comparison
					case (isset($associations[$i]) && ($item =
$menu->getItem($associations[$i]))):

						$language->link = JRoute::_('index.php?Itemid=' .
$item->id . '&lang=' . $language->sef);
						break;

					// Too bad...
					default:
						unset($languages[$i]);
				}
			}

			// If there are at least 2 of them, add the rel="alternate"
links to the <head>
			if (count($languages) > 1)
			{
				// Remove the sef from the default language if "Remove URL
Language Code" is on
				if ($remove_default_prefix &&
isset($languages[$this->default_lang]))
				{
					$languages[$this->default_lang]->link
									= preg_replace('|/' .
$languages[$this->default_lang]->sef . '/|', '/',
$languages[$this->default_lang]->link, 1);
				}

				foreach ($languages as $i => $language)
				{
					$doc->addHeadLink($server . $language->link,
'alternate', 'rel', array('hreflang' =>
$i));
				}

				// Add x-default language tag
				if ($this->params->get('xdefault', 1))
				{
					$xdefault_language =
$this->params->get('xdefault_language',
$this->default_lang);
					$xdefault_language = ($xdefault_language === 'default') ?
$this->default_lang : $xdefault_language;

					if (isset($languages[$xdefault_language]))
					{
						// Use a custom tag because addHeadLink is limited to one URI per tag
						$doc->addCustomTag('<link href="' . $server .
$languages[$xdefault_language]->link . '"
rel="alternate" hreflang="x-default" />');
					}
				}
			}
		}
	}

	/**
	 * Set the language cookie
	 *
	 * @param   string  $languageCode  The language code for which we want to
set the cookie
	 *
	 * @return  void
	 *
	 * @since   3.4.2
	 */
	private function setLanguageCookie($languageCode)
	{
		// If is set to use language cookie for a year in plugin params, save the
user language in a new cookie.
		if ((int) $this->params->get('lang_cookie', 0) === 1)
		{
			// Create a cookie with one year lifetime.
			$this->app->input->cookie->set(
				JApplicationHelper::getHash('language'),
				$languageCode,
				time() + 365 * 86400,
				$this->app->get('cookie_path', '/'),
				$this->app->get('cookie_domain', ''),
				$this->app->isHttpsForced(),
				true
			);
		}
		// If not, set the user language in the session (that is already saved in
a cookie).
		else
		{
			JFactory::getSession()->set('plg_system_languagefilter.language',
$languageCode);
		}
	}

	/**
	 * Get the language cookie
	 *
	 * @return  string
	 *
	 * @since   3.4.2
	 */
	private function getLanguageCookie()
	{
		// Is is set to use a year language cookie in plugin params, get the user
language from the cookie.
		if ((int) $this->params->get('lang_cookie', 0) === 1)
		{
			$languageCode =
$this->app->input->cookie->get(JApplicationHelper::getHash('language'));
		}
		// Else get the user language from the session.
		else
		{
			$languageCode =
JFactory::getSession()->get('plg_system_languagefilter.language');
		}

		// Let's be sure we got a valid language code. Fallback to null.
		if (!array_key_exists($languageCode, $this->lang_codes))
		{
			$languageCode = null;
		}

		return $languageCode;
	}
}
PK|��[aVOuss!languagefilter/languagefilter.xmlnu�[���<?xml
version="1.0" encoding="utf-8"?>
<extension version="3.1" type="plugin"
group="system" method="upgrade">
	<name>plg_system_languagefilter</name>
	<author>Joomla! Project</author>
	<creationDate>July 2010</creationDate>
	<copyright>(C) 2010 Open Source Matters, Inc.</copyright>
	<license>GNU General Public License version 2 or later; see
LICENSE.txt</license>
	<authorEmail>admin@joomla.org</authorEmail>
	<authorUrl>www.joomla.org</authorUrl>
	<version>3.0.0</version>
	<description>PLG_SYSTEM_LANGUAGEFILTER_XML_DESCRIPTION</description>
	<files>
		<filename
plugin="languagefilter">languagefilter.php</filename>
	</files>
	<languages>
		<language
tag="en-GB">en-GB.plg_system_languagefilter.ini</language>
		<language
tag="en-GB">en-GB.plg_system_languagefilter.sys.ini</language>
	</languages>
	<config>
		<fields name="params">
			<fieldset name="basic">
				<field
					name="detect_browser"
					type="list"
					label="PLG_SYSTEM_LANGUAGEFILTER_FIELD_DETECT_BROWSER_LABEL"
					description="PLG_SYSTEM_LANGUAGEFILTER_FIELD_DETECT_BROWSER_DESC"
					default="0"
					filter="integer"
					>
					<option
value="0">PLG_SYSTEM_LANGUAGEFILTER_SITE_LANGUAGE</option>
					<option
value="1">PLG_SYSTEM_LANGUAGEFILTER_BROWSER_SETTINGS</option>
				</field>

				<field
					name="automatic_change"
					type="radio"
					label="PLG_SYSTEM_LANGUAGEFILTER_FIELD_AUTOMATIC_CHANGE_LABEL"
					description="PLG_SYSTEM_LANGUAGEFILTER_FIELD_AUTOMATIC_CHANGE_DESC"
					default="1"
					filter="integer"
					class="btn-group btn-group-yesno"
					>
					<option value="1">JYES</option>
					<option value="0">JNO</option>
				</field>

				<field
					name="item_associations"
					type="radio"
					label="PLG_SYSTEM_LANGUAGEFILTER_FIELD_ITEM_ASSOCIATIONS_LABEL"
					description="PLG_SYSTEM_LANGUAGEFILTER_FIELD_ITEM_ASSOCIATIONS_DESC"
					default="1"
					filter="integer"
					class="btn-group btn-group-yesno"
					>
					<option value="1">JYES</option>
					<option value="0">JNO</option>
				</field>

				<field
					name="alternate_meta"
					type="radio"
					label="PLG_SYSTEM_LANGUAGEFILTER_FIELD_ALTERNATE_META_LABEL"
					description="PLG_SYSTEM_LANGUAGEFILTER_FIELD_ALTERNATE_META_DESC"
					default="1"
					filter="integer"
					class="btn-group btn-group-yesno"
					>
					<option value="1">JYES</option>
					<option value="0">JNO</option>
				</field>

				<field
					name="xdefault"
					type="radio"
					label="PLG_SYSTEM_LANGUAGEFILTER_FIELD_XDEFAULT_LABEL"
					description="PLG_SYSTEM_LANGUAGEFILTER_FIELD_XDEFAULT_DESC"
					default="1"
					filter="integer"
					class="btn-group btn-group-yesno"
					showon="alternate_meta:1"
					>
					<option value="1">JYES</option>
					<option value="0">JNO</option>
				</field>

				<field
					name="xdefault_language"
					type="contentlanguage"
					label="PLG_SYSTEM_LANGUAGEFILTER_FIELD_XDEFAULT_LANGUAGE_LABEL"
					description="PLG_SYSTEM_LANGUAGEFILTER_FIELD_XDEFAULT_LANGUAGE_DESC"
					default="default"
					showon="alternate_meta:1[AND]xdefault:1"
					>
					<option
value="default">PLG_SYSTEM_LANGUAGEFILTER_OPTION_DEFAULT_LANGUAGE</option>
				</field>

				<field
					name="remove_default_prefix"
					type="radio"
					label="PLG_SYSTEM_LANGUAGEFILTER_FIELD_REMOVE_DEFAULT_PREFIX_LABEL"
					description="PLG_SYSTEM_LANGUAGEFILTER_FIELD_REMOVE_DEFAULT_PREFIX_DESC"
					default="0"
					filter="integer"
					class="btn-group btn-group-yesno"
					>
					<option value="1">JYES</option>
					<option value="0">JNO</option>
				</field>

				<field
					name="lang_cookie"
					type="list"
					label="PLG_SYSTEM_LANGUAGEFILTER_FIELD_COOKIE_LABEL"
					description="PLG_SYSTEM_LANGUAGEFILTER_FIELD_COOKIE_DESC"
					default="0"
					filter="integer"
					>
					<option
value="1">PLG_SYSTEM_LANGUAGEFILTER_OPTION_YEAR</option>
					<option
value="0">PLG_SYSTEM_LANGUAGEFILTER_OPTION_SESSION</option>
				</field>
			</fieldset>
		</fields>
	</config>
</extension>PK}��[~���log/log.phpnu�[���<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.log
 *
 * @copyright   (C) 2007 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Joomla! System Logging Plugin.
 *
 * @since  1.5
 */
class PlgSystemLog extends JPlugin
{
	/**
	 * Called if user fails to be logged in.
	 *
	 * @param   array  $response  Array of response data.
	 *
	 * @return  void
	 *
	 * @since   1.5
	 */
	public function onUserLoginFailure($response)
	{
		$errorlog = array();

		switch ($response['status'])
		{
			case JAuthentication::STATUS_SUCCESS:
				$errorlog['status']  = $response['type'] . '
CANCELED: ';
				$errorlog['comment'] = $response['error_message'];
				break;

			case JAuthentication::STATUS_FAILURE:
				$errorlog['status']  = $response['type'] . '
FAILURE: ';

				if ($this->params->get('log_username', 0))
				{
					$errorlog['comment'] = $response['error_message']
. ' ("' . $response['username'] .
'")';
				}
				else
				{
					$errorlog['comment'] = $response['error_message'];
				}
				break;

			default:
				$errorlog['status']  = $response['type'] . '
UNKNOWN ERROR: ';
				$errorlog['comment'] = $response['error_message'];
				break;
		}

		JLog::addLogger(array(), JLog::INFO);

		try
		{
			JLog::add($errorlog['comment'], JLog::INFO,
$errorlog['status']);
		}
		catch (Exception $e)
		{
			// If the log file is unwriteable during login then we should not go to
the error page
			return;
		}
	}
}
PK}��[�$K��log/log.xmlnu�[���<?xml
version="1.0" encoding="utf-8"?>
<extension version="3.1" type="plugin"
group="system" method="upgrade">
	<name>plg_system_log</name>
	<author>Joomla! Project</author>
	<creationDate>April 2007</creationDate>
	<copyright>(C) 2007 Open Source Matters, Inc.</copyright>
	<license>GNU General Public License version 2 or later; see
LICENSE.txt</license>
	<authorEmail>admin@joomla.org</authorEmail>
	<authorUrl>www.joomla.org</authorUrl>
	<version>3.0.0</version>
	<description>PLG_LOG_XML_DESCRIPTION</description>
	<files>
		<filename plugin="log">log.php</filename>
	</files>
	<languages>
		<language
tag="en-GB">en-GB.plg_system_log.ini</language>
		<language
tag="en-GB">en-GB.plg_system_log.sys.ini</language>
	</languages>
	<config>
		<fields name="params">
			<fieldset name="basic">
				<field
					name="log_username"
					type="radio"
					label="PLG_SYSTEM_LOG_FIELD_LOG_USERNAME_LABEL"
					description="PLG_SYSTEM_LOG_FIELD_LOG_USERNAME_DESC"
					class="btn-group btn-group-yesno"
					default="0"
					filter="integer"
					>
					<option value="1">JYES</option>
					<option value="0">JNO</option>
				</field>
			</fieldset>
		</fields>
	</config>
</extension>
PK}��[[�G%�
�
logout/logout.phpnu�[���<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.logout
 *
 * @copyright   (C) 2010 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Plugin class for logout redirect handling.
 *
 * @since  1.6
 */
class PlgSystemLogout extends JPlugin
{
	/**
	 * Application object.
	 *
	 * @var    JApplicationCms
	 * @since  3.7.3
	 */
	protected $app;

	/**
	 * Load the language file on instantiation.
	 *
	 * @var    boolean
	 * @since  3.1
	 */
	protected $autoloadLanguage = true;

	/**
	 * Constructor.
	 *
	 * @param   object  &$subject  The object to observe -- event
dispatcher.
	 * @param   object  $config    An optional associative array of
configuration settings.
	 *
	 * @since   1.6
	 */
	public function __construct(&$subject, $config)
	{
		parent::__construct($subject, $config);

		// If we are on admin don't process.
		if (!$this->app->isClient('site'))
		{
			return;
		}

		$hash  = JApplicationHelper::getHash('PlgSystemLogout');

		if ($this->app->input->cookie->getString($hash))
		{
			// Destroy the cookie.
			$this->app->input->cookie->set($hash, '', 1,
$this->app->get('cookie_path', '/'),
$this->app->get('cookie_domain', ''));

			// Set the error handler for E_ALL to be the class handleError method.
			JError::setErrorHandling(E_ALL, 'callback',
array('PlgSystemLogout', 'handleError'));
		}
	}

	/**
	 * Method to handle any logout logic and report back to the subject.
	 *
	 * @param   array  $user     Holds the user data.
	 * @param   array  $options  Array holding options (client, ...).
	 *
	 * @return  boolean  Always returns true.
	 *
	 * @since   1.6
	 */
	public function onUserLogout($user, $options = array())
	{
		if ($this->app->isClient('site'))
		{
			// Create the cookie.
			$this->app->input->cookie->set(
				JApplicationHelper::getHash('PlgSystemLogout'),
				true,
				time() + 86400,
				$this->app->get('cookie_path', '/'),
				$this->app->get('cookie_domain', ''),
				$this->app->isHttpsForced(),
				true
			);
		}

		return true;
	}

	/**
	 * Method to handle an error condition.
	 *
	 * @param   Exception  &$error  The Exception object to be handled.
	 *
	 * @return  void
	 *
	 * @since   1.6
	 */
	public static function handleError(&$error)
	{
		// Get the application object.
		$app = JFactory::getApplication();

		// Make sure the error is a 403 and we are in the frontend.
		if ($error->getCode() == 403 &&
$app->isClient('site'))
		{
			// Redirect to the home page.
			$app->enqueueMessage(JText::_('PLG_SYSTEM_LOGOUT_REDIRECT'));
			$app->redirect('index.php');
		}
		else
		{
			// Render the custom error page.
			JError::customErrorPage($error);
		}
	}
}
PK}��[��C�logout/logout.xmlnu�[���<?xml
version="1.0" encoding="utf-8"?>
<extension version="3.1" type="plugin"
group="system" method="upgrade">
	<name>plg_system_logout</name>
	<author>Joomla! Project</author>
	<creationDate>April 2009</creationDate>
	<copyright>(C) 2009 Open Source Matters, Inc.</copyright>
	<license>GNU General Public License version 2 or later; see
LICENSE.txt</license>
	<authorEmail>admin@joomla.org</authorEmail>
	<authorUrl>www.joomla.org</authorUrl>
	<version>3.0.0</version>
	<description>PLG_SYSTEM_LOGOUT_XML_DESCRIPTION</description>
	<files>
		<filename plugin="logout">logout.php</filename>
	</files>
	<languages>
		<language
tag="en-GB">en-GB.plg_system_logout.ini</language>
		<language
tag="en-GB">en-GB.plg_system_logout.sys.ini</language>
	</languages>
</extension>
PK}��[X�/9��logrotation/logrotation.phpnu�[���<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.logrotation
 *
 * @copyright   (C) 2018 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\Filesystem\File;
use Joomla\Filesystem\Folder;
use Joomla\Filesystem\Path;

/**
 * Joomla! Log Rotation plugin
 *
 * Rotate the log files created by Joomla core
 *
 * @since  3.9.0
 */
class PlgSystemLogrotation extends JPlugin
{
	/**
	 * Load the language file on instantiation.
	 *
	 * @var    boolean
	 * @since  3.9.0
	 */
	protected $autoloadLanguage = true;

	/**
	 * Application object.
	 *
	 * @var    JApplicationCms
	 * @since  3.9.0
	 */
	protected $app;

	/**
	 * Database object.
	 *
	 * @var    JDatabaseDriver
	 * @since  3.9.0
	 */
	protected $db;

	/**
	 * The log check and rotation code is triggered after the page has fully
rendered.
	 *
	 * @return  void
	 *
	 * @since   3.9.0
	 */
	public function onAfterRender()
	{
		// Get the timeout as configured in plugin parameters

		/** @var \Joomla\Registry\Registry $params */
		$cache_timeout = (int) $this->params->get('cachetimeout',
30);
		$cache_timeout = 24 * 3600 * $cache_timeout;
		$logsToKeep    = (int) $this->params->get('logstokeep',
1);

		// Do we need to run? Compare the last run timestamp stored in the
plugin's options with the current
		// timestamp. If the difference is greater than the cache timeout we
shall not execute again.
		$now  = time();
		$last = (int) $this->params->get('lastrun', 0);

		if ((abs($now - $last) < $cache_timeout))
		{
			return;
		}

		// Update last run status
		$this->params->set('lastrun', $now);

		$db    = $this->db;
		$query = $db->getQuery(true)
			->update($db->qn('#__extensions'))
			->set($db->qn('params') . ' = ' .
$db->q($this->params->toString('JSON')))
			->where($db->qn('type') . ' = ' .
$db->q('plugin'))
			->where($db->qn('folder') . ' = ' .
$db->q('system'))
			->where($db->qn('element') . ' = ' .
$db->q('logrotation'));

		try
		{
			// Lock the tables to prevent multiple plugin executions causing a race
condition
			$db->lockTable('#__extensions');
		}
		catch (Exception $e)
		{
			// If we can't lock the tables it's too risky to continue
execution
			return;
		}

		try
		{
			// Update the plugin parameters
			$result = $db->setQuery($query)->execute();

			$this->clearCacheGroups(array('com_plugins'), array(0, 1));
		}
		catch (Exception $exc)
		{
			// If we failed to execute
			$db->unlockTables();
			$result = false;
		}

		try
		{
			// Unlock the tables after writing
			$db->unlockTables();
		}
		catch (Exception $e)
		{
			// If we can't lock the tables assume we have somehow failed
			$result = false;
		}

		// Abort on failure
		if (!$result)
		{
			return;
		}

		// Get the log path
		$logPath = Path::clean($this->app->get('log_path'));

		// Invalid path, stop processing further
		if (!is_dir($logPath))
		{
			return;
		}

		$logFiles = $this->getLogFiles($logPath);

		// Sort log files by version number in reserve order
		krsort($logFiles, SORT_NUMERIC);

		foreach ($logFiles as $version => $files)
		{
			if ($version >= $logsToKeep)
			{
				// Delete files which has version greater than or equals $logsToKeep
				foreach ($files as $file)
				{
					File::delete($logPath . '/' . $file);
				}
			}
			else
			{
				// For files which has version smaller than $logsToKeep, rotate
(increase version number)
				foreach ($files as $file)
				{
					$this->rotate($logPath, $file, $version);
				}
			}
		}
	}

	/**
	 * Get log files from log folder
	 *
	 * @param   string  $path  The folder to get log files
	 *
	 * @return  array   The log files in the given path grouped by version
number (not rotated files has number 0)
	 *
	 * @since   3.9.0
	 */
	private function getLogFiles($path)
	{
		$logFiles = array();
		$files    = Folder::files($path, '\.php$');

		foreach ($files as $file)
		{
			$parts    = explode('.', $file);

			/*
			 * Rotated log file has this filename format [VERSION].[FILENAME].php.
So if $parts has at least 3 elements
			 * and the first element is a number, we know that it's a rotated
file and can get it's current version
			 */
			if (count($parts) >= 3 && is_numeric($parts[0]))
			{
				$version = (int) $parts[0];
			}
			else
			{
				$version = 0;
			}

			if (!isset($logFiles[$version]))
			{
				$logFiles[$version] = array();
			}

			$logFiles[$version][] = $file;
		}

		return $logFiles;
	}

	/**
	 * Method to rotate (increase version) of a log file
	 *
	 * @param   string  $path            Path to file to rotate
	 * @param   string  $filename        Name of file to rotate
	 * @param   int     $currentVersion  The current version number
	 *
	 * @return  void
	 *
	 * @since   3.9.0
	 */
	private function rotate($path, $filename, $currentVersion)
	{
		if ($currentVersion === 0)
		{
			$rotatedFile = $path . '/1.' . $filename;
		}
		else
		{
			/*
			 * Rotated log file has this filename format [VERSION].[FILENAME].php.
To rotate it, we just need to explode
			 * the filename into an array, increase value of first element (keep
version) and implode it back to get the
			 * rotated file name
			 */
			$parts    = explode('.', $filename);
			$parts[0] = $currentVersion + 1;

			$rotatedFile = $path . '/' . implode('.', $parts);
		}

		File::move($path . '/' . $filename, $rotatedFile);
	}

	/**
	 * Clears cache groups. We use it to clear the plugins cache after we
update the last run timestamp.
	 *
	 * @param   array  $clearGroups   The cache groups to clean
	 * @param   array  $cacheClients  The cache clients (site, admin) to clean
	 *
	 * @return  void
	 *
	 * @since   3.9.0
	 */
	private function clearCacheGroups(array $clearGroups, array $cacheClients
= array(0, 1))
	{
		$conf = JFactory::getConfig();

		foreach ($clearGroups as $group)
		{
			foreach ($cacheClients as $client_id)
			{
				try
				{
					$options = array(
						'defaultgroup' => $group,
						'cachebase'    => $client_id ? JPATH_ADMINISTRATOR .
'/cache' :
							$conf->get('cache_path', JPATH_SITE .
'/cache')
					);

					$cache = JCache::getInstance('callback', $options);
					$cache->clean();
				}
				catch (Exception $e)
				{
					// Ignore it
				}
			}
		}
	}
}
PK}��[�g�##logrotation/logrotation.xmlnu�[���<?xml
version="1.0" encoding="utf-8"?>
<extension version="3.9" type="plugin"
group="system" method="upgrade">
	<name>plg_system_logrotation</name>
	<author>Joomla! Project</author>
	<creationDate>May 2018</creationDate>
	<copyright>(C) 2018 Open Source Matters, Inc.</copyright>
	<license>GNU General Public License version 2 or later; see
LICENSE.txt</license>
	<authorEmail>admin@joomla.org</authorEmail>
	<authorUrl>www.joomla.org</authorUrl>
	<version>3.9.0</version>
	<description>PLG_SYSTEM_LOGROTATION_XML_DESCRIPTION</description>
	<files>
		<filename
plugin="logrotation">logrotation.php</filename>
	</files>
	<languages folder="language">
		<language
tag="en-GB">en-GB.plg_system_logrotation.ini</language>
		<language
tag="en-GB">en-GB.plg_system_logrotation.sys.ini</language>
	</languages>
	<config>
		<fields name="params">
			<fieldset name="basic">
				<field
					name="cachetimeout"
					type="integer"
					label="PLG_SYSTEM_LOGROTATION_CACHETIMEOUT_LABEL"
					description="PLG_SYSTEM_LOGROTATION_CACHETIMEOUT_DESC"
					first="0"
					last="120"
					step="1"
					default="30"
					filter="int"
					validate="number"
				/>

				<field
					name="logstokeep"
					type="integer"
					label="PLG_SYSTEM_LOGROTATION_LOGSTOKEEP_LABEL"
					description="PLG_SYSTEM_LOGROTATION_LOGSTOKEEP_DESC"
					first="1"
					last="10"
					step="1"
					default="1"
					filter="int"
					validate="number"
				/>

				<field
					name="lastrun"
					type="hidden"
					default="0"
					filter="integer"
				/>
			</fieldset>
		</fields>
	</config>
</extension>
PK}��[�g��p3p/p3p.phpnu�[���<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.p3p
 *
 * @copyright   (C) 2010 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Joomla! P3P Header Plugin.
 *
 * @since  1.6
 * @deprecate  4.0  Obsolete
 */
class PlgSystemP3p extends JPlugin
{
	/**
	 * After initialise.
	 *
	 * @return  void
	 *
	 * @since   1.6
	 * @deprecate  4.0  Obsolete
	 */
	public function onAfterInitialise()
	{
		// Get the header.
		$header = $this->params->get('header', 'NOI ADM DEV
PSAi COM NAV OUR OTRo STP IND DEM');
		$header = trim($header);

		// Bail out on empty header (why would anyone do that?!).
		if (empty($header))
		{
			return;
		}

		// Replace any existing P3P headers in the response.
		JFactory::getApplication()->setHeader('P3P',
'CP="' . $header . '"', true);
	}
}
PK}��[�!$$p3p/p3p.xmlnu�[���<?xml
version="1.0" encoding="utf-8"?>
<extension version="3.1" type="plugin"
group="system" method="upgrade">
	<name>plg_system_p3p</name>
	<author>Joomla! Project</author>
	<creationDate>September 2010</creationDate>
	<copyright>(C) 2010 Open Source Matters, Inc.</copyright>
	<license>GNU General Public License version 2 or later; see
LICENSE.txt</license>
	<authorEmail>admin@joomla.org</authorEmail>
	<authorUrl>www.joomla.org</authorUrl>
	<version>3.0.0</version>
	<description>PLG_P3P_XML_DESCRIPTION</description>
	<files>
		<filename plugin="p3p">p3p.php</filename>
	</files>
	<languages>
		<language
tag="en-GB">en-GB.plg_system_p3p.ini</language>
		<language
tag="en-GB">en-GB.plg_system_p3p.sys.ini</language>
	</languages>
	<config>
		<fields name="params">
			<fieldset name="basic">
				<field
					name="header"
					type="text"
					label="PLG_P3P_HEADER_LABEL"
					description="PLG_P3P_HEADER_DESCRIPTION"
					default="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"
					size="37"
				/>
			</fieldset>
		</fields>
	</config>
</extension>
PK}��[��ɢ�
�
 privacyconsent/field/privacy.phpnu�[���<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.privacyconsent
 *
 * @copyright   (C) 2018 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('JPATH_PLATFORM') or die;

use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;

JFormHelper::loadFieldClass('radio');

/**
 * Provides input for privacy
 *
 * @since  3.9.0
 */
class JFormFieldprivacy extends JFormFieldRadio
{
	/**
	 * The form field type.
	 *
	 * @var    string
	 * @since  3.9.0
	 */
	protected $type = 'privacy';

	/**
	 * Method to get the field input markup.
	 *
	 * @return  string   The field input markup.
	 *
	 * @since   3.9.0
	 */
	protected function getInput()
	{
		// Display the message before the field
		echo
$this->getRenderer('plugins.system.privacyconsent.message')->render($this->getLayoutData());

		return parent::getInput();
	}

	/**
	 * Method to get the field label markup.
	 *
	 * @return  string  The field label markup.
	 *
	 * @since   3.9.0
	 */
	protected function getLabel()
	{
		if ($this->hidden)
		{
			return '';
		}

		return
$this->getRenderer('plugins.system.privacyconsent.label')->render($this->getLayoutData());

	}

	/**
	 * Method to get the data to be passed to the layout for rendering.
	 *
	 * @return  array
	 *
	 * @since   3.9.4
	 */
	protected function getLayoutData()
	{
		$data = parent::getLayoutData();

		$article = false;
		$privacyArticle = $this->element['article'] > 0 ? (int)
$this->element['article'] : 0;

		if ($privacyArticle &&
Factory::getApplication()->isClient('site'))
		{
			$db    = Factory::getDbo();
			$query = $db->getQuery(true)
				->select($db->quoteName(array('id', 'alias',
'catid', 'language')))
				->from($db->quoteName('#__content'))
				->where($db->quoteName('id') . ' = ' . (int)
$privacyArticle);
			$db->setQuery($query);
			$article = $db->loadObject();

			JLoader::register('ContentHelperRoute', JPATH_BASE .
'/components/com_content/helpers/route.php');

			$slug = $article->alias ? ($article->id . ':' .
$article->alias) : $article->id;
			$article->link  = ContentHelperRoute::getArticleRoute($slug,
$article->catid, $article->language);
		}

		$extraData = array(
			'privacynote' =>
!empty($this->element['note']) ?
$this->element['note'] :
Text::_('PLG_SYSTEM_PRIVACYCONSENT_NOTE_FIELD_DEFAULT'),
			'options' => $this->getOptions(),
			'value'   => (string) $this->value,
			'translateLabel' => $this->translateLabel,
			'translateDescription' => $this->translateDescription,
			'translateHint' => $this->translateHint,
			'privacyArticle' => $privacyArticle,
			'article' => $article,
		);

		return array_merge($data, $extraData);
	}
}
PK}��[�h=::0privacyconsent/privacyconsent/privacyconsent.xmlnu�[���<?xml
version="1.0" encoding="utf-8"?>
<form>
	<fields name="privacyconsent">
		<fieldset
			name="privacyconsent"
			label="PLG_SYSTEM_PRIVACYCONSENT_LABEL"
		>
			<field
				name="privacy"
				type="privacy"
				label="PLG_SYSTEM_PRIVACYCONSENT_FIELD_LABEL"
				description="PLG_SYSTEM_PRIVACYCONSENT_FIELD_DESC"
				default="0"
				filter="integer"
				required="true"
				>
				<option
value="1">PLG_SYSTEM_PRIVACYCONSENT_OPTION_AGREE</option>
				<option
value="0">PLG_SYSTEM_PRIVACYCONSENT_OPTION_DO_NOT_AGREE</option>
			</field>
		</fieldset>
	</fields>
</form>
PK}��[1���MM!privacyconsent/privacyconsent.phpnu�[���<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.privacyconsent
 *
 * @copyright   (C) 2018 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;
use Joomla\Utilities\ArrayHelper;

/**
 * An example custom privacyconsent plugin.
 *
 * @since  3.9.0
 */
class PlgSystemPrivacyconsent extends JPlugin
{
	/**
	 * Load the language file on instantiation.
	 *
	 * @var    boolean
	 * @since  3.9.0
	 */
	protected $autoloadLanguage = true;

	/**
	 * Application object.
	 *
	 * @var    JApplicationCms
	 * @since  3.9.0
	 */
	protected $app;

	/**
	 * Database object.
	 *
	 * @var    JDatabaseDriver
	 * @since  3.9.0
	 */
	protected $db;

	/**
	 * Constructor
	 *
	 * @param   object  &$subject  The object to observe
	 * @param   array   $config    An array that holds the plugin
configuration
	 *
	 * @since   3.9.0
	 */
	public function __construct(&$subject, $config)
	{
		parent::__construct($subject, $config);

		JFormHelper::addFieldPath(__DIR__ . '/field');
	}

	/**
	 * Adds additional fields to the user editing form
	 *
	 * @param   JForm  $form  The form to be altered.
	 * @param   mixed  $data  The associated data for the form.
	 *
	 * @return  boolean
	 *
	 * @since   3.9.0
	 */
	public function onContentPrepareForm($form, $data)
	{
		if (!($form instanceof JForm))
		{
			$this->_subject->setError('JERROR_NOT_A_FORM');

			return false;
		}

		// Check we are manipulating a valid form - we only display this on user
registration form and user profile form.
		$name = $form->getName();

		if (!in_array($name, array('com_users.profile',
'com_users.registration')))
		{
			return true;
		}

		// We only display this if user has not consented before
		if (is_object($data))
		{
			$userId = isset($data->id) ? $data->id : 0;

			if ($userId > 0 && $this->isUserConsented($userId))
			{
				return true;
			}
		}

		// Add the privacy policy fields to the form.
		JForm::addFormPath(__DIR__ . '/privacyconsent');
		$form->loadFile('privacyconsent');

		$privacyArticleId = $this->getPrivacyArticleId();
		$privacynote      = $this->params->get('privacy_note');

		// Push the privacy article ID into the privacy field.
		$form->setFieldAttribute('privacy', 'article',
$privacyArticleId, 'privacyconsent');
		$form->setFieldAttribute('privacy', 'note',
$privacynote, 'privacyconsent');
	}

	/**
	 * Method is called before user data is stored in the database
	 *
	 * @param   array    $user   Holds the old user data.
	 * @param   boolean  $isNew  True if a new user is stored.
	 * @param   array    $data   Holds the new user data.
	 *
	 * @return  boolean
	 *
	 * @since   3.9.0
	 * @throws  InvalidArgumentException on missing required data.
	 */
	public function onUserBeforeSave($user, $isNew, $data)
	{
		// // Only check for front-end user creation/update profile
		if ($this->app->isClient('administrator'))
		{
			return true;
		}

		$userId = ArrayHelper::getValue($user, 'id', 0,
'int');

		// User already consented before, no need to check it further
		if ($userId > 0 && $this->isUserConsented($userId))
		{
			return true;
		}

		// Check that the privacy is checked if required ie only in registration
from frontend.
		$option = $this->app->input->getCmd('option');
		$task   = $this->app->input->get->getCmd('task');
		$form   = $this->app->input->post->get('jform',
array(), 'array');

		if ($option == 'com_users' && in_array($task,
array('registration.register', 'profile.save'))
			&&
empty($form['privacyconsent']['privacy']))
		{
			throw new
InvalidArgumentException(Text::_('PLG_SYSTEM_PRIVACYCONSENT_FIELD_ERROR'));
		}

		return true;
	}

	/**
	 * Saves user privacy confirmation
	 *
	 * @param   array    $data    entered user data
	 * @param   boolean  $isNew   true if this is a new user
	 * @param   boolean  $result  true if saving the user worked
	 * @param   string   $error   error message
	 *
	 * @return  boolean
	 *
	 * @since   3.9.0
	 */
	public function onUserAfterSave($data, $isNew, $result, $error)
	{
		// Only create an entry on front-end user creation/update profile
		if ($this->app->isClient('administrator'))
		{
			return true;
		}

		// Get the user's ID
		$userId = ArrayHelper::getValue($data, 'id', 0,
'int');

		// If user already consented before, no need to check it further
		if ($userId > 0 && $this->isUserConsented($userId))
		{
			return true;
		}

		$option = $this->app->input->getCmd('option');
		$task   = $this->app->input->get->getCmd('task');
		$form   = $this->app->input->post->get('jform',
array(), 'array');

		if ($option == 'com_users'
			&&in_array($task, array('registration.register',
'profile.save'))
			&&
!empty($form['privacyconsent']['privacy']))
		{
			$userId = ArrayHelper::getValue($data, 'id', 0,
'int');

			// Get the user's IP address
			$ip =
$this->app->input->server->get('REMOTE_ADDR',
'', 'string');

			// Get the user agent string
			$userAgent =
$this->app->input->server->get('HTTP_USER_AGENT',
'', 'string');

			// Create the user note
			$userNote = (object) array(
				'user_id' => $userId,
				'subject' =>
'PLG_SYSTEM_PRIVACYCONSENT_SUBJECT',
				'body'    =>
Text::sprintf('PLG_SYSTEM_PRIVACYCONSENT_BODY', $ip, $userAgent),
				'created' => Factory::getDate()->toSql(),
			);

			try
			{
				$this->db->insertObject('#__privacy_consents',
$userNote);
			}
			catch (Exception $e)
			{
				// Do nothing if the save fails
			}

			$userId = ArrayHelper::getValue($data, 'id', 0,
'int');

			$message = array(
				'action'      => 'consent',
				'id'          => $userId,
				'title'       => $data['name'],
				'itemlink'    =>
'index.php?option=com_users&task=user.edit&id=' .
$userId,
				'userid'      => $userId,
				'username'    => $data['username'],
				'accountlink' =>
'index.php?option=com_users&task=user.edit&id=' .
$userId,
			);

			JModelLegacy::addIncludePath(JPATH_ADMINISTRATOR .
'/components/com_actionlogs/models',
'ActionlogsModel');

			/* @var ActionlogsModelActionlog $model */
			$model = JModelLegacy::getInstance('Actionlog',
'ActionlogsModel');
			$model->addLog(array($message),
'PLG_SYSTEM_PRIVACYCONSENT_CONSENT',
'plg_system_privacyconsent', $userId);
		}

		return true;
	}

	/**
	 * Remove all user privacy consent information for the given user ID
	 *
	 * Method is called after user data is deleted from the database
	 *
	 * @param   array    $user     Holds the user data
	 * @param   boolean  $success  True if user was successfully stored in the
database
	 * @param   string   $msg      Message
	 *
	 * @return  boolean
	 *
	 * @since   3.9.0
	 */
	public function onUserAfterDelete($user, $success, $msg)
	{
		if (!$success)
		{
			return false;
		}

		$userId = ArrayHelper::getValue($user, 'id', 0,
'int');

		if ($userId)
		{
			// Remove user's consent
			try
			{
				$query = $this->db->getQuery(true)
					->delete($this->db->quoteName('#__privacy_consents'))
					->where($this->db->quoteName('user_id') . ' =
' . (int) $userId);
				$this->db->setQuery($query);
				$this->db->execute();
			}
			catch (Exception $e)
			{
				$this->_subject->setError($e->getMessage());

				return false;
			}
		}

		return true;
	}

	/**
	 * If logged in users haven't agreed to privacy consent, redirect
them to profile edit page, ask them to agree to
	 * privacy consent before allowing access to any other pages
	 *
	 * @return  void
	 *
	 * @since   3.9.0
	 */
	public function onAfterRoute()
	{
		// Run this in frontend only
		if ($this->app->isClient('administrator'))
		{
			return;
		}

		$userId = Factory::getUser()->id;

		// Check to see whether user already consented, if not, redirect to user
profile page
		if ($userId > 0)
		{
			// If user consented before, no need to check it further
			if ($this->isUserConsented($userId))
			{
				return;
			}

			$option = $this->app->input->getCmd('option');
			$task   = $this->app->input->get('task');
			$view   = $this->app->input->getString('view',
'');
			$layout = $this->app->input->getString('layout',
'');
			$id     = $this->app->input->getInt('id');

			$privacyArticleId = $this->getPrivacyArticleId();

			/*
			 * If user is already on edit profile screen or view privacy article
			 * or press update/apply button, or logout, do nothing to avoid infinite
redirect
			 */
			if ($option == 'com_users' && in_array($task,
array('profile.save', 'profile.apply',
'user.logout', 'user.menulogout'))
				|| ($option == 'com_content' && $view ==
'article' && $id == $privacyArticleId)
				|| ($option == 'com_users' && $view ==
'profile' && $layout == 'edit'))
			{
				return;
			}

			// Redirect to com_users profile edit
			$this->app->enqueueMessage($this->getRedirectMessage(),
'notice');
			$link =
'index.php?option=com_users&view=profile&layout=edit';
			$this->app->redirect(\JRoute::_($link, false));
		}
	}

	/**
	 * Event to specify whether a privacy policy has been published.
	 *
	 * @param   array  &$policy  The privacy policy status data, passed by
reference, with keys "published", "editLink" and
"articlePublished".
	 *
	 * @return  void
	 *
	 * @since   3.9.0
	 */
	public function onPrivacyCheckPrivacyPolicyPublished(&$policy)
	{
		// If another plugin has already indicated a policy is published, we
won't change anything here
		if ($policy['published'])
		{
			return;
		}

		$articleId = $this->params->get('privacy_article');

		if (!$articleId)
		{
			return;
		}

		// Check if the article exists in database and is published
		$query = $this->db->getQuery(true)
			->select($this->db->quoteName(array('id',
'state')))
			->from($this->db->quoteName('#__content'))
			->where($this->db->quoteName('id') . ' = '
. (int) $articleId);
		$this->db->setQuery($query);

		$article = $this->db->loadObject();

		// Check if the article exists
		if (!$article)
		{
			return;
		}

		// Check if the article is published
		if ($article->state == 1)
		{
			$policy['articlePublished'] = true;
		}

		$policy['published'] = true;
		$policy['editLink']  =
JRoute::_('index.php?option=com_content&task=article.edit&id='
. $articleId);
	}

	/**
	 * Returns the configured redirect message and falls back to the default
version.
	 *
	 * @return  string  redirect message
	 *
	 * @since   3.9.0
	 */
	private function getRedirectMessage()
	{
		$messageOnRedirect =
trim($this->params->get('messageOnRedirect',
''));

		if (empty($messageOnRedirect))
		{
			return
Text::_('PLG_SYSTEM_PRIVACYCONSENT_REDIRECT_MESSAGE_DEFAULT');
		}

		return $messageOnRedirect;
	}

	/**
	 * Method to check if the given user has consented yet
	 *
	 * @param   integer  $userId  ID of uer to check
	 *
	 * @return  boolean
	 *
	 * @since   3.9.0
	 */
	private function isUserConsented($userId)
	{
		$query = $this->db->getQuery(true);
		$query->select('COUNT(*)')
			->from('#__privacy_consents')
			->where('user_id = ' . (int) $userId)
			->where('subject = ' .
$this->db->quote('PLG_SYSTEM_PRIVACYCONSENT_SUBJECT'))
			->where('state = 1');
		$this->db->setQuery($query);

		return (int) $this->db->loadResult() > 0;
	}

	/**
	 * Get privacy article ID. If the site is a multilingual website and there
is associated article for the
	 * current language, ID of the associated article will be returned
	 *
	 * @return  integer
	 *
	 * @since   3.9.0
	 */
	private function getPrivacyArticleId()
	{
		$privacyArticleId =
$this->params->get('privacy_article');

		if ($privacyArticleId > 0 &&
JLanguageAssociations::isEnabled())
		{
			$privacyAssociated =
JLanguageAssociations::getAssociations('com_content',
'#__content', 'com_content.item', $privacyArticleId);
			$currentLang = JFactory::getLanguage()->getTag();

			if (isset($privacyAssociated[$currentLang]))
			{
				$privacyArticleId = $privacyAssociated[$currentLang]->id;
			}
		}

		return $privacyArticleId;
	}

	/**
	 * The privacy consent expiration check code is triggered after the page
has fully rendered.
	 *
	 * @return  void
	 *
	 * @since   3.9.0
	 */
	public function onAfterRender()
	{
		if (!$this->params->get('enabled', 0))
		{
			return;
		}

		$cacheTimeout = (int) $this->params->get('cachetimeout',
30);
		$cacheTimeout = 24 * 3600 * $cacheTimeout;

		// Do we need to run? Compare the last run timestamp stored in the
plugin's options with the current
		// timestamp. If the difference is greater than the cache timeout we
shall not execute again.
		$now  = time();
		$last = (int) $this->params->get('lastrun', 0);

		if ((abs($now - $last) < $cacheTimeout))
		{
			return;
		}

		// Update last run status
		$this->params->set('lastrun', $now);
		$db    = $this->db;
		$query = $db->getQuery(true)
			->update($db->quoteName('#__extensions'))
			->set($db->quoteName('params') . ' = ' .
$db->quote($this->params->toString('JSON')))
			->where($db->quoteName('type') . ' = ' .
$db->quote('plugin'))
			->where($db->quoteName('folder') . ' = ' .
$db->quote('system'))
			->where($db->quoteName('element') . ' = ' .
$db->quote('privacyconsent'));

		try
		{
			// Lock the tables to prevent multiple plugin executions causing a race
condition
			$db->lockTable('#__extensions');
		}
		catch (Exception $e)
		{
			// If we can't lock the tables it's too risky to continue
execution
			return;
		}

		try
		{
			// Update the plugin parameters
			$result = $db->setQuery($query)->execute();
			$this->clearCacheGroups(array('com_plugins'), array(0, 1));
		}
		catch (Exception $exc)
		{
			// If we failed to execute
			$db->unlockTables();
			$result = false;
		}

		try
		{
			// Unlock the tables after writing
			$db->unlockTables();
		}
		catch (Exception $e)
		{
			// If we can't lock the tables assume we have somehow failed
			$result = false;
		}

		// Abort on failure
		if (!$result)
		{
			return;
		}

		// Delete the expired privacy consents
		$this->invalidateExpiredConsents();

		// Remind for privacy consents near to expire
		$this->remindExpiringConsents();

	}

	/**
	 * Method to send the remind for privacy consents renew
	 *
	 * @return  integer
	 *
	 * @since   3.9.0
	 */
	private function remindExpiringConsents()
	{
		// Load the parameters.
		$expire = (int) $this->params->get('consentexpiration',
365);
		$remind = (int) $this->params->get('remind', 30);
		$now    = JFactory::getDate()->toSql();
		$period = '-' . ($expire - $remind);

		$db    = $this->db;
		$query = $db->getQuery(true)
			->select($db->quoteName(array('r.id',
'r.user_id', 'u.email')))
			->from($db->quoteName('#__privacy_consents',
'r'))
			->leftJoin($db->quoteName('#__users', 'u') .
' ON u.id = r.user_id')
			->where($db->quoteName('subject') . ' = ' .
$db->quote('PLG_SYSTEM_PRIVACYCONSENT_SUBJECT'))
			->where($db->quoteName('remind') . ' = 0');
		$query->where($query->dateAdd($db->quote($now), $period,
'DAY') . ' > ' .
$db->quoteName('created'));

		try
		{
			$users = $db->setQuery($query)->loadObjectList();
		}
		catch (JDatabaseException $exception)
		{
			return false;
		}

		$app      = JFactory::getApplication();
		$linkMode = $app->get('force_ssl', 0) == 2 ?
Route::TLS_FORCE : Route::TLS_IGNORE;

		foreach ($users as $user)
		{
			$token       =
JApplicationHelper::getHash(JUserHelper::genRandomPassword());
			$hashedToken = JUserHelper::hashPassword($token);

			// The mail
			try
			{
				$substitutions = array(
					'[SITENAME]' => $app->get('sitename'),
					'[URL]'      => JUri::root(),
					'[TOKENURL]' => JRoute::link('site',
'index.php?option=com_privacy&view=remind&remind_token='
. $token, false, $linkMode, true),
					'[FORMURL]'  => JRoute::link('site',
'index.php?option=com_privacy&view=remind', false, $linkMode,
true),
					'[TOKEN]'    => $token,
					'\\n'        => "\n",
				);

				$emailSubject =
JText::_('PLG_SYSTEM_PRIVACYCONSENT_EMAIL_REMIND_SUBJECT');
				$emailBody =
JText::_('PLG_SYSTEM_PRIVACYCONSENT_EMAIL_REMIND_BODY');

				foreach ($substitutions as $k => $v)
				{
					$emailSubject = str_replace($k, $v, $emailSubject);
					$emailBody    = str_replace($k, $v, $emailBody);
				}

				$mailer = JFactory::getMailer();
				$mailer->setSubject($emailSubject);
				$mailer->setBody($emailBody);
				$mailer->addRecipient($user->email);

				$mailResult = $mailer->Send();

				if ($mailResult instanceof JException)
				{
					return false;
				}
				elseif ($mailResult === false)
				{
					return false;
				}

				// Update the privacy_consents item to not send the reminder again
				$query->clear()
					->update($db->quoteName('#__privacy_consents'))
					->set($db->quoteName('remind') . ' = 1 ')
					->set($db->quoteName('token') . ' = ' .
$db->quote($hashedToken))
					->where($db->quoteName('id') . ' = ' . (int)
$user->id);
				$db->setQuery($query);

				try
				{
					$db->execute();
				}
				catch (RuntimeException $e)
				{
					return false;
				}
			}
			catch (phpmailerException $exception)
			{
				return false;
			}
		}
	}

	/**
	 * Method to delete the expired privacy consents
	 *
	 * @return  boolean
	 *
	 * @since   3.9.0
	 */
	private function invalidateExpiredConsents()
	{
		// Load the parameters.
		$expire = (int) $this->params->get('consentexpiration',
365);
		$now    = JFactory::getDate()->toSql();
		$period = '-' . $expire;

		$db    = $this->db;
		$query = $db->getQuery(true);
		$query->select($db->quoteName(array('id',
'user_id')))
			->from($db->quoteName('#__privacy_consents'))
			->where($query->dateAdd($db->quote($now), $period,
'DAY') . ' > ' .
$db->quoteName('created'))
			->where($db->quoteName('subject') . ' = ' .
$db->quote('PLG_SYSTEM_PRIVACYCONSENT_SUBJECT'))
			->where($db->quoteName('state') . ' = 1');
		$db->setQuery($query);

		try
		{
			$users = $db->loadObjectList();
		}
		catch (RuntimeException $e)
		{
			return false;
		}

		// Do not process further if no expired consents found
		if (empty($users))
		{
			return true;
		}

		// Push a notification to the site's super users
		JModelLegacy::addIncludePath(JPATH_ADMINISTRATOR .
'/components/com_messages/models', 'MessagesModel');
		JTable::addIncludePath(JPATH_ADMINISTRATOR .
'/components/com_messages/tables');
		/** @var MessagesModelMessage $messageModel */
		$messageModel = JModelLegacy::getInstance('Message',
'MessagesModel');

		foreach ($users as $user)
		{
			$query = $db->getQuery(true)
				->update($db->quoteName('#__privacy_consents'))
				->set('state = 0')
				->where($db->quoteName('id') . ' = ' . (int)
$user->id);
			$db->setQuery($query);

			try
			{
				$db->execute();
			}
			catch (RuntimeException $e)
			{
				return false;
			}

			$messageModel->notifySuperUsers(
				JText::_('PLG_SYSTEM_PRIVACYCONSENT_NOTIFICATION_USER_PRIVACY_EXPIRED_SUBJECT'),
				JText::sprintf('PLG_SYSTEM_PRIVACYCONSENT_NOTIFICATION_USER_PRIVACY_EXPIRED_MESSAGE',
JFactory::getUser($user->user_id)->username)
			);
		}

		return true;
	}
	/**
	 * Clears cache groups. We use it to clear the plugins cache after we
update the last run timestamp.
	 *
	 * @param   array  $clearGroups   The cache groups to clean
	 * @param   array  $cacheClients  The cache clients (site, admin) to clean
	 *
	 * @return  void
	 *
	 * @since    3.9.0
	 */
	private function clearCacheGroups(array $clearGroups, array $cacheClients
= array(0, 1))
	{
		$conf = JFactory::getConfig();

		foreach ($clearGroups as $group)
		{
			foreach ($cacheClients as $client_id)
			{
				try
				{
					$options = array(
						'defaultgroup' => $group,
						'cachebase'    => $client_id ? JPATH_ADMINISTRATOR .
'/cache' :
							$conf->get('cache_path', JPATH_SITE .
'/cache')
					);

					$cache = JCache::getInstance('callback', $options);
					$cache->clean();
				}
				catch (Exception $e)
				{
					// Ignore it
				}
			}
		}
	}
}
PK}��[�UqGJ
J
!privacyconsent/privacyconsent.xmlnu�[���<?xml
version="1.0" encoding="utf-8"?>
<extension version="3.9" type="plugin"
group="system" method="upgrade">
	<name>plg_system_privacyconsent</name>
	<author>Joomla! Project</author>
	<creationDate>April 2018</creationDate>
	<copyright>(C) 2018 Open Source Matters, Inc.</copyright>
	<license>GNU General Public License version 2 or later; see
LICENSE.txt</license>
	<authorEmail>admin@joomla.org</authorEmail>
	<authorUrl>www.joomla.org</authorUrl>
	<version>3.9.0</version>
	<description>PLG_SYSTEM_PRIVACYCONSENT_XML_DESCRIPTION</description>
	<files>
		<filename
plugin="privacyconsent">privacyconsent.php</filename>
		<folder>privacyconsent</folder>
		<folder>field</folder>
	</files>
	<languages>
		<language
tag="en-GB">en-GB.plg_system_privacyconsent.ini</language>
		<language
tag="en-GB">en-GB.plg_system_privacyconsent.sys.ini</language>
	</languages>
	<config>
		<fields name="params">
			<fieldset name="basic"
addfieldpath="/administrator/components/com_content/models/fields">
				<field
					name="privacy_note"
					type="textarea"
					label="PLG_SYSTEM_PRIVACYCONSENT_NOTE_FIELD_LABEL"
					description="PLG_SYSTEM_PRIVACYCONSENT_NOTE_FIELD_DESC"
					hint="PLG_SYSTEM_PRIVACYCONSENT_NOTE_FIELD_DEFAULT"
					class="span12"
					rows="7"
					cols="20"
					filter="html"
				/>
				<field
					name="privacy_article"
					type="modal_article"
					label="PLG_SYSTEM_PRIVACYCONSENT_FIELD_ARTICLE_LABEL"
					description="PLG_SYSTEM_PRIVACYCONSENT_FIELD_ARTICLE_DESC"
					select="true"
					new="true"
					edit="true"
					clear="true"
					filter="integer"
				/>
				<field
					name="messageOnRedirect"
					type="textarea"
					label="PLG_SYSTEM_PRIVACYCONSENT_REDIRECT_MESSAGE_LABEL"
					description="PLG_SYSTEM_PRIVACYCONSENT_REDIRECT_MESSAGE_DESC"
					hint="PLG_SYSTEM_PRIVACYCONSENT_REDIRECT_MESSAGE_DEFAULT"
					class="span12"
					rows="7"
					cols="20"
					filter="html"
				/>
			</fieldset>
			<fieldset
				name="expiration"
				label="PLG_SYSTEM_PRIVACYCONSENT_EXPIRATION_FIELDSET_LABEL"
			>
				<field
					name="enabled"
					type="radio"
					label="PLG_SYSTEM_PRIVACYCONSENT_FIELD_ENABLED_LABEL"
					description="PLG_SYSTEM_PRIVACYCONSENT_FIELD_ENABLED_DESC"
					class="btn-group btn-group-yesno"
					default="0"
					filter="integer"
					>
					<option value="1">JYES</option>
					<option value="0">JNO</option>
				</field>
				<field
					name="cachetimeout"
					type="integer"
					label="PLG_SYSTEM_PRIVACYCONSENT_CACHETIMEOUT_LABEL"
					description="PLG_SYSTEM_PRIVACYCONSENT_CACHETIMEOUT_DESC"
					first="0"
					last="120"
					step="1"
					default="30"
					filter="int"
					validate="number"
				/>
				<field
					name="consentexpiration"
					type="integer"
					label="PLG_SYSTEM_PRIVACYCONSENT_CONSENTEXPIRATION_LABEL"
					description="PLG_SYSTEM_PRIVACYCONSENT_CONSENTEXPIRATION_DESC"
					first="180"
					last="720"
					step="30"
					default="360"
					filter="int"
					validate="number"
				/>
				<field
					name="remind"
					type="integer"
					label="PLG_SYSTEM_PRIVACYCONSENT_REMINDBEFORE_LABEL"
					description="PLG_SYSTEM_PRIVACYCONSENT_REMINDBEFORE_DESC"
					first="0"
					last="120"
					step="1"
					default="30"
					filter="int"
					validate="number"
				/>
				<field
					name="lastrun"
					type="hidden"
					default="0"
					filter="integer"
				/>
			</fieldset>
		</fields>
	</config>
</extension>
PK}��[1�r��redirect/form/excludes.xmlnu�[���<?xml
version="1.0" encoding="utf-8"?>
<form>
	<fieldset>
		<field
			name="term"
			type="text"
			label="PLG_SYSTEM_REDIRECT_FIELD_EXCLUDE_URLS_TERM_LABEL"
			description="PLG_SYSTEM_REDIRECT_FIELD_EXCLUDE_URLS_TERM_DESC"
			required="true"
		/>
		<field
			name="regexp"
			type="checkbox"
			label="PLG_SYSTEM_REDIRECT_FIELD_EXCLUDE_URLS_REGEXP_LABEL"
			description="PLG_SYSTEM_REDIRECT_FIELD_EXCLUDE_URLS_REGEXP_DESC"
			filter="integer"
		/>
	</fieldset>
</form>
PK}��[�6j�%&%&redirect/redirect.phpnu�[���<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.redirect
 *
 * @copyright   (C) 2009 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\Registry\Registry;
use Joomla\String\StringHelper;

/**
 * Plugin class for redirect handling.
 *
 * @since  1.6
 */
class PlgSystemRedirect extends JPlugin
{
	/**
	 * Affects constructor behavior. If true, language files will be loaded
automatically.
	 *
	 * @var    boolean
	 * @since  3.4
	 */
	protected $autoloadLanguage = false;

	/**
	 * The global exception handler registered before the plugin was
instantiated
	 *
	 * @var    callable
	 * @since  3.6
	 */
	private static $previousExceptionHandler;

	/**
	 * Constructor.
	 *
	 * @param   object  &$subject  The object to observe
	 * @param   array   $config    An optional associative array of
configuration settings.
	 *
	 * @since   1.6
	 */
	public function __construct(&$subject, $config)
	{
		parent::__construct($subject, $config);

		// Set the JError handler for E_ERROR to be the class' handleError
method.
		JError::setErrorHandling(E_ERROR, 'callback',
array('PlgSystemRedirect', 'handleError'));

		// Register the previously defined exception handler so we can forward
errors to it
		self::$previousExceptionHandler =
set_exception_handler(array('PlgSystemRedirect',
'handleException'));
	}

	/**
	 * Method to handle an error condition from JError.
	 *
	 * @param   JException  $error  The JException object to be handled.
	 *
	 * @return  void
	 *
	 * @since   1.6
	 */
	public static function handleError(JException $error)
	{
		self::doErrorHandling($error);
	}

	/**
	 * Method to handle an uncaught exception.
	 *
	 * @param   Exception|Throwable  $exception  The Exception or Throwable
object to be handled.
	 *
	 * @return  void
	 *
	 * @since   3.5
	 * @throws  InvalidArgumentException
	 */
	public static function handleException($exception)
	{
		// If this isn't a Throwable then bail out
		if (!($exception instanceof Throwable) && !($exception instanceof
Exception))
		{
			throw new InvalidArgumentException(
				sprintf('The error handler requires an Exception or Throwable
object, a "%s" object was given instead.',
get_class($exception))
			);
		}

		self::doErrorHandling($exception);
	}

	/**
	 * Internal processor for all error handlers
	 *
	 * @param   Exception|Throwable  $error  The Exception or Throwable object
to be handled.
	 *
	 * @return  void
	 *
	 * @since   3.5
	 */
	private static function doErrorHandling($error)
	{
		$app = JFactory::getApplication();

		if ($app->isClient('administrator') || ((int)
$error->getCode() !== 404))
		{
			// Proxy to the previous exception handler if available, otherwise just
render the error page
			if (self::$previousExceptionHandler)
			{
				call_user_func_array(self::$previousExceptionHandler, array($error));
			}
			else
			{
				JErrorPage::render($error);
			}
		}

		$uri = JUri::getInstance();

		// These are the original URLs
		$orgurl                =
rawurldecode($uri->toString(array('scheme', 'host',
'port', 'path', 'query',
'fragment')));
		$orgurlRel             =
rawurldecode($uri->toString(array('path', 'query',
'fragment')));

		// The above doesn't work for sub directories, so do this
		$orgurlRootRel         = str_replace(JUri::root(), '',
$orgurl);

		// For when users have added / to the url
		$orgurlRootRelSlash    = str_replace(JUri::root(), '/',
$orgurl);
		$orgurlWithoutQuery    =
rawurldecode($uri->toString(array('scheme', 'host',
'port', 'path', 'fragment')));
		$orgurlRelWithoutQuery =
rawurldecode($uri->toString(array('path',
'fragment')));

		// These are the URLs we save and use
		$url                =
StringHelper::strtolower(rawurldecode($uri->toString(array('scheme',
'host', 'port', 'path', 'query',
'fragment'))));
		$urlRel             =
StringHelper::strtolower(rawurldecode($uri->toString(array('path',
'query', 'fragment'))));

		// The above doesn't work for sub directories, so do this
		$urlRootRel         = str_replace(JUri::root(), '', $url);

		// For when users have added / to the url
		$urlRootRelSlash    = str_replace(JUri::root(), '/', $url);
		$urlWithoutQuery    =
StringHelper::strtolower(rawurldecode($uri->toString(array('scheme',
'host', 'port', 'path',
'fragment'))));
		$urlRelWithoutQuery =
StringHelper::strtolower(rawurldecode($uri->toString(array('path',
'fragment'))));

		$plugin = JPluginHelper::getPlugin('system',
'redirect');

		$params = new Registry($plugin->params);

		$excludes = (array) $params->get('exclude_urls');

		$skipUrl = false;

		foreach ($excludes as $exclude)
		{
			if (empty($exclude->term))
			{
				continue;
			}

			if (!empty($exclude->regexp))
			{
				// Only check $url, because it includes all other sub urls
				if (preg_match('/' . $exclude->term . '/i',
$orgurlRel))
				{
					$skipUrl = true;
					break;
				}
			}
			else
			{
				if (StringHelper::strpos($orgurlRel, $exclude->term) !== false)
				{
					$skipUrl = true;
					break;
				}
			}
		}

		// Why is this (still) here?
		if ($skipUrl || (strpos($url, 'mosConfig_') !== false) ||
(strpos($url, '=http://') !== false))
		{
			JErrorPage::render($error);
		}

		$db = JFactory::getDbo();

		$query = $db->getQuery(true);

		$query->select('*')
			->from($db->quoteName('#__redirect_links'))
			->where(
				'('
				. $db->quoteName('old_url') . ' = ' .
$db->quote($url)
				. ' OR '
				. $db->quoteName('old_url') . ' = ' .
$db->quote($urlRel)
				. ' OR '
				. $db->quoteName('old_url') . ' = ' .
$db->quote($urlRootRel)
				. ' OR '
				. $db->quoteName('old_url') . ' = ' .
$db->quote($urlRootRelSlash)
				. ' OR '
				. $db->quoteName('old_url') . ' = ' .
$db->quote($urlWithoutQuery)
				. ' OR '
				. $db->quoteName('old_url') . ' = ' .
$db->quote($urlRelWithoutQuery)
				. ' OR '
				. $db->quoteName('old_url') . ' = ' .
$db->quote($orgurl)
				. ' OR '
				. $db->quoteName('old_url') . ' = ' .
$db->quote($orgurlRel)
				. ' OR '
				. $db->quoteName('old_url') . ' = ' .
$db->quote($orgurlRootRel)
				. ' OR '
				. $db->quoteName('old_url') . ' = ' .
$db->quote($orgurlRootRelSlash)
				. ' OR '
				. $db->quoteName('old_url') . ' = ' .
$db->quote($orgurlWithoutQuery)
				. ' OR '
				. $db->quoteName('old_url') . ' = ' .
$db->quote($orgurlRelWithoutQuery)
				. ')'
			);

		$db->setQuery($query);

		$redirect = null;

		try
		{
			$redirects = $db->loadAssocList();
		}
		catch (Exception $e)
		{
			JErrorPage::render(new
Exception(JText::_('PLG_SYSTEM_REDIRECT_ERROR_UPDATING_DATABASE'),
500, $e));
		}

		$possibleMatches = array_unique(
			array(
				$url,
				$urlRel,
				$urlRootRel,
				$urlRootRelSlash,
				$urlWithoutQuery,
				$urlRelWithoutQuery,
				$orgurl,
				$orgurlRel,
				$orgurlRootRel,
				$orgurlRootRelSlash,
				$orgurlWithoutQuery,
				$orgurlRelWithoutQuery,
			)
		);

		foreach ($possibleMatches as $match)
		{
			if (($index = array_search($match, array_column($redirects,
'old_url'))) !== false)
			{
				$redirect = (object) $redirects[$index];

				if ((int) $redirect->published === 1)
				{
					break;
				}
			}
		}

		// A redirect object was found and, if published, will be used
		if ($redirect !== null && ((int) $redirect->published === 1))
		{
			if (!$redirect->header || (bool)
JComponentHelper::getParams('com_redirect')->get('mode',
false) === false)
			{
				$redirect->header = 301;
			}

			if ($redirect->header < 400 && $redirect->header >=
300)
			{
				$urlQuery = $uri->getQuery();

				$oldUrlParts = parse_url($redirect->old_url);

				$newUrl = $redirect->new_url;

				if ($urlQuery !== '' &&
empty($oldUrlParts['query']))
				{
					$newUrl .= '?' . $urlQuery;
				}

				$dest = JUri::isInternal($newUrl) || strpos($newUrl, 'http')
=== false ?
					JRoute::_($newUrl) : $newUrl;

				// In case the url contains double // lets remove it
				$destination = str_replace(JUri::root() . '/', JUri::root(),
$dest);

				// Always count redirect hits
				$redirect->hits++;

				try
				{
					$db->updateObject('#__redirect_links', $redirect,
'id');
				}
				catch (Exception $e)
				{
					// We don't log issues for now
				}

				$app->redirect($destination, (int) $redirect->header);
			}

			JErrorPage::render(new RuntimeException($error->getMessage(),
$redirect->header, $error));
		}
		// No redirect object was found so we create an entry in the redirect
table
		elseif ($redirect === null)
		{
			$params = new Registry(JPluginHelper::getPlugin('system',
'redirect')->params);

			if ((bool) $params->get('collect_urls', 1))
			{
				if (!$params->get('includeUrl', 1))
				{
					$url = $urlRel;
				}

				$data = (object) array(
					'id' => 0,
					'old_url' => $url,
					'referer' =>
$app->input->server->getString('HTTP_REFERER',
''),
					'hits' => 1,
					'published' => 0,
					'created_date' => JFactory::getDate()->toSql()
				);

				try
				{
					$db->insertObject('#__redirect_links', $data,
'id');
				}
				catch (Exception $e)
				{
					JErrorPage::render(new
Exception(JText::_('PLG_SYSTEM_REDIRECT_ERROR_UPDATING_DATABASE'),
500, $e));
				}
			}
		}
		// We have an unpublished redirect object, increment the hit counter
		else
		{
			$redirect->hits++;

			try
			{
				$db->updateObject('#__redirect_links', $redirect,
'id');
			}
			catch (Exception $e)
			{
				JErrorPage::render(new
Exception(JText::_('PLG_SYSTEM_REDIRECT_ERROR_UPDATING_DATABASE'),
500, $e));
			}
		}

		// Proxy to the previous exception handler if available, otherwise just
render the error page
		if (self::$previousExceptionHandler)
		{
			call_user_func_array(self::$previousExceptionHandler, array($error));
		}
		else
		{
			JErrorPage::render($error);
		}
	}
}
PK}��[-LS�TTredirect/redirect.xmlnu�[���<?xml
version="1.0" encoding="utf-8"?>
<extension version="3.1" type="plugin"
group="system" method="upgrade">
	<name>plg_system_redirect</name>
	<author>Joomla! Project</author>
	<creationDate>April 2009</creationDate>
	<copyright>(C) 2009 Open Source Matters, Inc.</copyright>
	<license>GNU General Public License version 2 or later; see
LICENSE.txt</license>
	<authorEmail>admin@joomla.org</authorEmail>
	<authorUrl>www.joomla.org</authorUrl>
	<version>3.0.0</version>
	<description>PLG_SYSTEM_REDIRECT_XML_DESCRIPTION</description>
	<files>
		<folder>form</folder>
		<filename plugin="redirect">redirect.php</filename>
	</files>
	<languages>
		<language
tag="en-GB">en-GB.plg_system_redirect.ini</language>
		<language
tag="en-GB">en-GB.plg_system_redirect.sys.ini</language>
	</languages>
	<config>
		<fields name="params">
			<fieldset name="basic">
				<field
					name="collect_urls"
					type="radio"
					label="PLG_SYSTEM_REDIRECT_FIELD_COLLECT_URLS_LABEL"
					description="PLG_SYSTEM_REDIRECT_FIELD_COLLECT_URLS_DESC"
					default="1"
					filter="integer"
					class="btn-group btn-group-yesno"
					>
					<option value="1">JENABLED</option>
					<option value="0">JDISABLED</option>
				</field>
				<field
					name="includeUrl"
					type="radio"
					label="PLG_SYSTEM_REDIRECT_FIELD_STORE_FULL_URL_LABEL"
					description="PLG_SYSTEM_REDIRECT_FIELD_STORE_FULL_URL_DESC"
					default="1"
					filter="integer"
					class="btn-group btn-group-yesno"
					>
					<option value="1">JYES</option>
					<option value="0">JNO</option>
				</field>
				<field
					name="exclude_urls"
					type="subform"
					label="PLG_SYSTEM_REDIRECT_FIELD_EXCLUDE_URLS_LABEL"
					description="PLG_SYSTEM_REDIRECT_FIELD_EXCLUDE_URLS_DESC"
					multiple="true"
					formsource="plugins/system/redirect/form/excludes.xml"
				/>
			</fieldset>
		</fields>
	</config>
</extension>
PK}��[���
�
remember/remember.phpnu�[���<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.remember
 *
 * @copyright   (C) 2007 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Joomla! System Remember Me Plugin
 *
 * @since  1.5
 */

class PlgSystemRemember extends JPlugin
{
	/**
	 * Application object.
	 *
	 * @var    JApplicationCms
	 * @since  3.2
	 */
	protected $app;

	/**
	 * Remember me method to run onAfterInitialise
	 * Only purpose is to initialise the login authentication process if a
cookie is present
	 *
	 * @return  void
	 *
	 * @since   1.5
	 * @throws  InvalidArgumentException
	 */
	public function onAfterInitialise()
	{
		// Get the application if not done by JPlugin. This may happen during
upgrades from Joomla 2.5.
		if (!$this->app)
		{
			$this->app = JFactory::getApplication();
		}

		// No remember me for admin.
		if ($this->app->isClient('administrator'))
		{
			return;
		}

		// Check for a cookie if user is not logged in
		if (JFactory::getUser()->get('guest'))
		{
			$cookieName = 'joomla_remember_me_' .
JUserHelper::getShortHashedUserAgent();

			// Try with old cookieName (pre 3.6.0) if not found
			if (!$this->app->input->cookie->get($cookieName))
			{
				$cookieName = JUserHelper::getShortHashedUserAgent();
			}

			// Check for the cookie
			if ($this->app->input->cookie->get($cookieName))
			{
				$this->app->login(array('username' => ''),
array('silent' => true));
			}
		}
	}

	/**
	 * Imports the authentication plugin on user logout to make sure that the
cookie is destroyed.
	 *
	 * @param   array  $user     Holds the user data.
	 * @param   array  $options  Array holding options (remember,
autoregister, group).
	 *
	 * @return  boolean
	 */
	public function onUserLogout($user, $options)
	{
		// No remember me for admin
		if ($this->app->isClient('administrator'))
		{
			return true;
		}

		$cookieName = 'joomla_remember_me_' .
JUserHelper::getShortHashedUserAgent();

		// Check for the cookie
		if ($this->app->input->cookie->get($cookieName))
		{
			// Make sure authentication group is loaded to process onUserAfterLogout
event
			JPluginHelper::importPlugin('authentication');
		}

		return true;
	}

	/**
	 * Method is called before user data is stored in the database
	 * Invalidate all existing remember-me cookies after a password change
	 *
	 * @param   array    $user   Holds the old user data.
	 * @param   boolean  $isnew  True if a new user is stored.
	 * @param   array    $data   Holds the new user data.
	 *
	 * @return    boolean
	 *
	 * @since   3.8.6
	 */
	public function onUserBeforeSave($user, $isnew, $data)
	{
		// Irrelevant on new users
		if ($isnew)
		{
			return true;
		}

		// Irrelevant, because password was not changed by user
		if (empty($data['password_clear']))
		{
			return true;
		}

		/*
		 * But now, we need to do something 
		 * Delete all tokens for this user!
		 */
		$db = JFactory::getDbo();
		$query = $db->getQuery(true)
			->delete('#__user_keys')
			->where($db->quoteName('user_id') . ' = ' .
$db->quote($user['username']));
		try
		{
			$db->setQuery($query)->execute();
		}
		catch (RuntimeException $e)
		{
			// Log an alert for the site admin
			JLog::add(
				sprintf('Failed to delete cookie token for user %s with the
following error: %s', $user['username'],
$e->getMessage()),
				JLog::WARNING,
				'security'
			);
		}

		return true;
	}
}
PK}��[�-��remember/remember.xmlnu�[���<?xml
version="1.0" encoding="utf-8"?>
<extension version="3.1" type="plugin"
group="system" method="upgrade">
	<name>plg_system_remember</name>
	<author>Joomla! Project</author>
	<creationDate>April 2007</creationDate>
	<copyright>(C) 2007 Open Source Matters, Inc.</copyright>
	<license>GNU General Public License version 2 or later; see
LICENSE.txt</license>
	<authorEmail>admin@joomla.org</authorEmail>
	<authorUrl>www.joomla.org</authorUrl>
	<version>3.0.0</version>
	<description>PLG_REMEMBER_XML_DESCRIPTION</description>
	<files>
		<filename plugin="remember">remember.php</filename>
	</files>
	<languages>
		<language
tag="en-GB">en-GB.plg_system_remember.ini</language>
		<language
tag="en-GB">en-GB.plg_system_remember.sys.ini</language>
	</languages>
</extension>
PK}��[?>� ��sef/sef.phpnu�[���<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.sef
 *
 * @copyright   (C) 2007 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Joomla! SEF Plugin.
 *
 * @since  1.5
 */
class PlgSystemSef extends JPlugin
{
	/**
	 * Application object.
	 *
	 * @var    JApplicationCms
	 * @since  3.5
	 */
	protected $app;

	/**
	 * Add the canonical uri to the head.
	 *
	 * @return  void
	 *
	 * @since   3.5
	 */
	public function onAfterDispatch()
	{
		$doc = $this->app->getDocument();

		if (!$this->app->isClient('site') || $doc->getType()
!== 'html')
		{
			return;
		}

		$sefDomain = $this->params->get('domain', false);

		// Don't add a canonical html tag if no alternative domain has added
in SEF plugin domain field.
		if (empty($sefDomain))
		{
			return;
		}

		// Check if a canonical html tag already exists (for instance, added by a
component).
		$canonical = '';

		foreach ($doc->_links as $linkUrl => $link)
		{
			if (isset($link['relation']) &&
$link['relation'] === 'canonical')
			{
				$canonical = $linkUrl;
				break;
			}
		}

		// If a canonical html tag already exists get the canonical and change it
to use the SEF plugin domain field.
		if (!empty($canonical))
		{
			// Remove current canonical link.
			unset($doc->_links[$canonical]);

			// Set the current canonical link but use the SEF system plugin domain
field.
			$canonical = $sefDomain .
JUri::getInstance($canonical)->toString(array('path',
'query', 'fragment'));
		}
		// If a canonical html doesn't exists already add a canonical html
tag using the SEF plugin domain field.
		else
		{
			$canonical = $sefDomain .
JUri::getInstance()->toString(array('path', 'query',
'fragment'));
		}

		// Add the canonical link.
		$doc->addHeadLink(htmlspecialchars($canonical),
'canonical');
	}

	/**
	 * Convert the site URL to fit to the HTTP request.
	 *
	 * @return  void
	 */
	public function onAfterRender()
	{
		if (!$this->app->isClient('site'))
		{
			return;
		}

		// Replace src links.
		$base   = JUri::base(true) . '/';
		$buffer = $this->app->getBody();

		// For feeds we need to search for the URL with domain.
		$prefix = $this->app->getDocument()->getType() ===
'feed' ? JUri::root() : '';

		// Replace index.php URI by SEF URI.
		if (strpos($buffer, 'href="' . $prefix .
'index.php?') !== false)
		{
			preg_match_all('#href="' . $prefix .
'index.php\?([^"]+)"#m', $buffer, $matches);

			foreach ($matches[1] as $urlQueryString)
			{
				$buffer = str_replace(
					'href="' . $prefix . 'index.php?' .
$urlQueryString . '"',
					'href="' . trim($prefix, '/') .
JRoute::_('index.php?' . $urlQueryString) . '"',
					$buffer
				);
			}

			$this->checkBuffer($buffer);
		}

		// Check for all unknown protocols (a protocol must contain at least one
alphanumeric character followed by a ":").
		$protocols  = '[a-zA-Z0-9\-]+:';
		$attributes = array('href=', 'src=',
'poster=');

		foreach ($attributes as $attribute)
		{
			if (strpos($buffer, $attribute) !== false)
			{
				$regex  = '#\s' . $attribute . '"(?!/|' .
$protocols . '|\#|\')([^"]*)"#m';
				$buffer = preg_replace($regex, ' ' . $attribute .
'"' . $base . '$1"', $buffer);
				$this->checkBuffer($buffer);
			}
		}

		if (strpos($buffer, 'srcset=') !== false)
		{
			$regex = '#\s+srcset="([^"]+)"#m';

			$buffer = preg_replace_callback(
				$regex,
				function ($match) use ($base, $protocols)
				{
					preg_match_all('#(?:[^\s]+)\s*(?:[\d\.]+[wx])?(?:\,\s*)?#i',
$match[1], $matches);

					foreach ($matches[0] as &$src)
					{
						$src = preg_replace('#^(?!/|' . $protocols .
'|\#|\')(.+)#', $base . '$1', $src);
					}

					return ' srcset="' . implode($matches[0]) .
'"';
				},
				$buffer
			);

			$this->checkBuffer($buffer);
		}

		// Replace all unknown protocols in javascript window open events.
		if (strpos($buffer, 'window.open(') !== false)
		{
			$regex  = '#onclick="window.open\(\'(?!/|' .
$protocols . '|\#)([^/]+[^\']*?\')#m';
			$buffer = preg_replace($regex,
'onclick="window.open(\'' . $base . '$1',
$buffer);
			$this->checkBuffer($buffer);
		}

		// Replace all unknown protocols in onmouseover and onmouseout
attributes.
		$attributes = array('onmouseover=', 'onmouseout=');

		foreach ($attributes as $attribute)
		{
			if (strpos($buffer, $attribute) !== false)
			{
				$regex  = '#' . $attribute .
'"this.src=([\']+)(?!/|' . $protocols .
'|\#|\')([^"]+)"#m';
				$buffer = preg_replace($regex, $attribute .
'"this.src=$1' . $base . '$2"', $buffer);
				$this->checkBuffer($buffer);
			}
		}

		// Replace all unknown protocols in CSS background image.
		if (strpos($buffer, 'style=') !== false)
		{
			$regex_url  =
'\s*url\s*\(([\'\"]|\&\#0?3[49];)?(?!/|\&\#0?3[49];|'
. $protocols .
'|\#)([^\)\'\"]+)([\'\"]|\&\#0?3[49];)?\)';
			$regex  = '#style=\s*([\'\"])(.*):' . $regex_url .
'#m';
			$buffer = preg_replace($regex, 'style=$1$2: url($3' . $base .
'$4$5)', $buffer);
			$this->checkBuffer($buffer);
		}

		// Replace all unknown protocols in OBJECT param tag.
		if (strpos($buffer, '<param') !== false)
		{
			// OBJECT <param name="xx", value="yy"> -- fix
it only inside the <param> tag.
			$regex  =
'#(<param\s+)name\s*=\s*"(movie|src|url)"[^>]\s*value\s*=\s*"(?!/|'
. $protocols . '|\#|\')([^"]*)"#m';
			$buffer = preg_replace($regex, '$1name="$2"
value="' . $base . '$3"', $buffer);
			$this->checkBuffer($buffer);

			// OBJECT <param value="xx", name="yy"> -- fix
it only inside the <param> tag.
			$regex  = '#(<param\s+[^>]*)value\s*=\s*"(?!/|' .
$protocols .
'|\#|\')([^"]*)"\s*name\s*=\s*"(movie|src|url)"#m';
			$buffer = preg_replace($regex, '<param value="' .
$base . '$2" name="$3"', $buffer);
			$this->checkBuffer($buffer);
		}

		// Replace all unknown protocols in OBJECT tag.
		if (strpos($buffer, '<object') !== false)
		{
			$regex  = '#(<object\s+[^>]*)data\s*=\s*"(?!/|' .
$protocols . '|\#|\')([^"]*)"#m';
			$buffer = preg_replace($regex, '$1data="' . $base .
'$2"', $buffer);
			$this->checkBuffer($buffer);
		}

		// Use the replaced HTML body.
		$this->app->setBody($buffer);
	}

	/**
	 * Check the buffer.
	 *
	 * @param   string  $buffer  Buffer to be checked.
	 *
	 * @return  void
	 */
	private function checkBuffer($buffer)
	{
		if ($buffer === null)
		{
			switch (preg_last_error())
			{
				case PREG_BACKTRACK_LIMIT_ERROR:
					$message = 'PHP regular expression limit reached
(pcre.backtrack_limit)';
					break;
				case PREG_RECURSION_LIMIT_ERROR:
					$message = 'PHP regular expression limit reached
(pcre.recursion_limit)';
					break;
				case PREG_BAD_UTF8_ERROR:
					$message = 'Bad UTF8 passed to PCRE function';
					break;
				default:
					$message = 'Unknown PCRE error calling PCRE function';
			}

			throw new RuntimeException($message);
		}
	}

	/**
	 * Replace the matched tags.
	 *
	 * @param   array  &$matches  An array of matches (see
preg_match_all).
	 *
	 * @return  string
	 *
	 * @deprecated  4.0  No replacement.
	 */
	protected static function route(&$matches)
	{
		JLog::add(__METHOD__ . ' is deprecated, no replacement.',
JLog::WARNING, 'deprecated');

		$url   = $matches[1];
		$url   = str_replace('&amp;', '&', $url);
		$route = JRoute::_('index.php?' . $url);

		return 'href="' . $route;
	}
}
PK}��[n*mf  sef/sef.xmlnu�[���<?xml
version="1.0" encoding="utf-8"?>
<extension version="3.1" type="plugin"
group="system" method="upgrade">
	<name>plg_system_sef</name>
	<author>Joomla! Project</author>
	<creationDate>December 2007</creationDate>
	<copyright>(C) 2007 Open Source Matters, Inc.</copyright>
	<license>GNU General Public License version 2 or later; see
LICENSE.txt</license>
	<authorEmail>admin@joomla.org</authorEmail>
	<authorUrl>www.joomla.org</authorUrl>
	<version>3.0.0</version>
	<description>PLG_SEF_XML_DESCRIPTION</description>
	<files>
		<filename plugin="sef">sef.php</filename>
	</files>
	<languages>
		<language
tag="en-GB">en-GB.plg_system_sef.ini</language>
		<language
tag="en-GB">en-GB.plg_system_sef.sys.ini</language>
	</languages>
	<config>
		<fields name="params">
			<fieldset name="basic">
				<field
					name="domain"
					type="url"
					label="PLG_SEF_DOMAIN_LABEL"
					description="PLG_SEF_DOMAIN_DESCRIPTION"
					hint="https://www.example.com"
					filter="url"
					validate="url"
				/>
			</fieldset>
		</fields>
	</config>
</extension>
PK}��[ҽ�F��sessiongc/sessiongc.phpnu�[���<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.sessiongc
 *
 * @copyright   (C) 2018 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\CMS\Application\CMSApplication;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Session\MetadataManager;

/**
 * Garbage collection handler for session related data
 *
 * @since  3.8.6
 */
class PlgSystemSessionGc extends CMSPlugin
{
	/**
	 * Application object
	 *
	 * @var    CMSApplication
	 * @since  3.8.6
	 */
	protected $app;

	/**
	 * Database driver
	 *
	 * @var    JDatabaseDriver
	 * @since  3.8.6
	 */
	protected $db;

	/**
	 * Runs after the HTTP response has been sent to the client and performs
garbage collection tasks
	 *
	 * @return  void
	 *
	 * @since   3.8.6
	 */
	public function onAfterRespond()
	{
		$session = Factory::getSession();

		if ($this->params->get('enable_session_gc', 1))
		{
			$probability = $this->params->get('gc_probability', 1);
			$divisor     = $this->params->get('gc_divisor', 100);

			$random = $divisor * lcg_value();

			if ($probability > 0 && $random < $probability)
			{
				$session->gc();
			}
		}

		if ($this->app->get('session_handler', 'none')
!== 'database' &&
$this->params->get('enable_session_metadata_gc', 1))
		{
			$probability = $this->params->get('gc_probability', 1);
			$divisor     = $this->params->get('gc_divisor', 100);

			$random = $divisor * lcg_value();

			if ($probability > 0 && $random < $probability)
			{
				$metadataManager = new MetadataManager($this->app, $this->db);
				$metadataManager->deletePriorTo(time() - $session->getExpire());
			}
		}
	}
}
PK}��[sPw��sessiongc/sessiongc.xmlnu�[���<?xml
version="1.0" encoding="utf-8"?>
<extension version="3.8" type="plugin"
group="system" method="upgrade">
	<name>plg_system_sessiongc</name>
	<author>Joomla! Project</author>
	<creationDate>February 2018</creationDate>
	<copyright>(C) 2018 Open Source Matters, Inc.</copyright>
	<license>GNU General Public License version 2 or later; see
LICENSE.txt</license>
	<authorEmail>admin@joomla.org</authorEmail>
	<authorUrl>www.joomla.org</authorUrl>
	<version>3.8.6</version>
	<description>PLG_SYSTEM_SESSIONGC_XML_DESCRIPTION</description>
	<files>
		<filename
plugin="sessiongc">sessiongc.php</filename>
	</files>
	<languages folder="language">
		<language
tag="en-GB">en-GB/en-GB.plg_system_sessiongc.ini</language>
		<language
tag="en-GB">en-GB/en-GB.plg_system_sessiongc.sys.ini</language>
	</languages>
	<config>
		<fields name="params">
			<fieldset name="basic">
				<field
					name="enable_session_gc"
					type="radio"
					label="PLG_SYSTEM_SESSIONGC_ENABLE_SESSION_GC_LABEL"
					description="PLG_SYSTEM_SESSIONGC_ENABLE_SESSION_GC_DESC"
					class="btn-group btn-group-yesno"
					default="1"
					filter="uint"
					>
					<option value="1">JYES</option>
					<option value="0">JNO</option>
				</field>

				<field
					name="enable_session_metadata_gc"
					type="radio"
					label="PLG_SYSTEM_SESSIONGC_ENABLE_SESSION_METADATA_GC_LABEL"
					description="PLG_SYSTEM_SESSIONGC_ENABLE_SESSION_METADATA_GC_DESC"
					class="btn-group btn-group-yesno"
					default="1"
					filter="uint"
					>
					<option value="1">JYES</option>
					<option value="0">JNO</option>
				</field>

				<field
					name="gc_probability"
					type="number"
					label="PLG_SYSTEM_SESSIONGC_GC_PROBABILITY_LABEL"
					description="PLG_SYSTEM_SESSIONGC_GC_PROBABILITY_DESC"
					filter="uint"
					validate="number"
					min="1"
					default="1"
					showon="enable_session_gc:1[OR]enable_session_metadata_gc:1"
				/>

				<field
					name="gc_divisor"
					type="number"
					label="PLG_SYSTEM_SESSIONGC_GC_DIVISOR_LABEL"
					description="PLG_SYSTEM_SESSIONGC_GC_DIVISOR_DESC"
					filter="uint"
					validate="number"
					min="1"
					default="100"
					showon="enable_session_gc:1[OR]enable_session_metadata_gc:1"
				/>
			</fieldset>
		</fields>
	</config>
</extension>
PK}��[�V���stats/field/base.phpnu�[���<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.stats
 *
 * @copyright   (C) 2016 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

/**
 * Base field for the Stats Plugin.
 *
 * @since  3.5
 */
abstract class PlgSystemStatsFormFieldBase extends JFormField
{
	/**
	 * Get the layouts paths
	 *
	 * @return  array
	 *
	 * @since   3.5
	 */
	protected function getLayoutPaths()
	{
		$template = JFactory::getApplication()->getTemplate();

		return array(
			JPATH_ADMINISTRATOR . '/templates/' . $template .
'/html/layouts/plugins/system/stats',
			dirname(__DIR__) . '/layouts',
			JPATH_SITE . '/layouts'
		);
	}
}
PK}��[G!E���stats/field/data.phpnu�[���<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.stats
 *
 * @copyright   (C) 2016 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

JLoader::register('PlgSystemStatsFormFieldBase', __DIR__ .
'/base.php');

/**
 * Unique ID Field class for the Stats Plugin.
 *
 * @since  3.5
 */
class PlgSystemStatsFormFieldData extends PlgSystemStatsFormFieldBase
{
	/**
	 * The form field type.
	 *
	 * @var    string
	 * @since  3.5
	 */
	protected $type = 'Data';

	/**
	 * Name of the layout being used to render the field
	 *
	 * @var    string
	 * @since  3.5
	 */
	protected $layout = 'field.data';

	/**
	 * Method to get the data to be passed to the layout for rendering.
	 *
	 * @return  array
	 *
	 * @since   3.5
	 */
	protected function getLayoutData()
	{
		$data       = parent::getLayoutData();

		$dispatcher = JEventDispatcher::getInstance();
		JPluginHelper::importPlugin('system', 'stats');

		$result = $dispatcher->trigger('onGetStatsData',
array('stats.field.data'));

		$data['statsData'] = $result ? reset($result) : array();

		return $data;
	}
}
PK}��[EN8��stats/field/uniqueid.phpnu�[���<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.stats
 *
 * @copyright   (C) 2016 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

JLoader::register('PlgSystemStatsFormFieldBase', __DIR__ .
'/base.php');

/**
 * Unique ID Field class for the Stats Plugin.
 *
 * @since  3.5
 */
class PlgSystemStatsFormFieldUniqueid extends PlgSystemStatsFormFieldBase
{
	/**
	 * The form field type.
	 *
	 * @var    string
	 * @since  3.5
	 */
	protected $type = 'Uniqueid';

	/**
	 * Name of the layout being used to render the field
	 *
	 * @var    string
	 * @since  3.5
	 */
	protected $layout = 'field.uniqueid';
}
PK��[�e����stats/layouts/field/data.phpnu�[���<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.stats
 *
 * @copyright   (C) 2016 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

extract($displayData);

/**
 * Layout variables
 * -----------------
 * @var   string   $autocomplete    Autocomplete attribute for the field.
 * @var   boolean  $autofocus       Is autofocus enabled?
 * @var   string   $class           Classes for the input.
 * @var   string   $description     Description of the field.
 * @var   boolean  $disabled        Is this field disabled?
 * @var   string   $group           Group the field belongs to.
<fields> section in form XML.
 * @var   boolean  $hidden          Is this field hidden in the form?
 * @var   string   $hint            Placeholder for the field.
 * @var   string   $id              DOM id of the field.
 * @var   string   $label           Label of the field.
 * @var   string   $labelclass      Classes to apply to the label.
 * @var   boolean  $multiple        Does this field support multiple
values?
 * @var   string   $name            Name of the input field.
 * @var   string   $onchange        Onchange attribute for the field.
 * @var   string   $onclick         Onclick attribute for the field.
 * @var   string   $pattern         Pattern (Reg Ex) of value of the form
field.
 * @var   boolean  $readonly        Is this field read only?
 * @var   boolean  $repeat          Allows extensions to duplicate
elements.
 * @var   boolean  $required        Is this field required?
 * @var   integer  $size            Size attribute of the input.
 * @var   boolean  $spellcheck      Spellcheck state for the form field.
 * @var   string   $validate        Validation rules to apply.
 * @var   string   $value           Value attribute of the field.
 * @var   array    $options         Options available for this field.
 * @var   array    $statsData       Statistics that will be sent to the
stats server
 */

JHtml::_('jquery.framework');
?>
<a href="#" onclick="jQuery(this).next().toggle(200);
return false;"><?php echo
JText::_('PLG_SYSTEM_STATS_MSG_WHAT_DATA_WILL_BE_SENT');
?></a>
<?php
echo $field->render('stats', compact('statsData'));
PK��[oT=		
stats/layouts/field/uniqueid.phpnu�[���<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.stats
 *
 * @copyright   (C) 2016 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

extract($displayData);

/**
 * Layout variables
 * -----------------
 * @var   string   $autocomplete    Autocomplete attribute for the field.
 * @var   boolean  $autofocus       Is autofocus enabled?
 * @var   string   $class           Classes for the input.
 * @var   string   $description     Description of the field.
 * @var   boolean  $disabled        Is this field disabled?
 * @var   string   $group           Group the field belongs to.
<fields> section in form XML.
 * @var   boolean  $hidden          Is this field hidden in the form?
 * @var   string   $hint            Placeholder for the field.
 * @var   string   $id              DOM id of the field.
 * @var   string   $label           Label of the field.
 * @var   string   $labelclass      Classes to apply to the label.
 * @var   boolean  $multiple        Does this field support multiple
values?
 * @var   string   $name            Name of the input field.
 * @var   string   $onchange        Onchange attribute for the field.
 * @var   string   $onclick         Onclick attribute for the field.
 * @var   string   $pattern         Pattern (Reg Ex) of value of the form
field.
 * @var   boolean  $readonly        Is this field read only?
 * @var   boolean  $repeat          Allows extensions to duplicate
elements.
 * @var   boolean  $required        Is this field required?
 * @var   integer  $size            Size attribute of the input.
 * @var   boolean  $spellcheck      Spellcheck state for the form field.
 * @var   string   $validate        Validation rules to apply.
 * @var   string   $value           Value attribute of the field.
 * @var   array    $options         Options available for this field.
 */
?>
<input type="hidden" name="<?php echo $name;
?>" id="<?php echo $id; ?>" value="<?php
echo htmlspecialchars($value, ENT_COMPAT, 'UTF-8'); ?>"
/>
<a class="btn"
onclick="document.getElementById('<?php echo $id;
?>').value='';Joomla.submitbutton('plugin.apply');">
	<span class="icon-refresh"></span> <?php echo
JText::_('PLG_SYSTEM_STATS_RESET_UNIQUE_ID'); ?>
</a>PK��[֙Qstats/layouts/message.phpnu�[���<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.stats
 *
 * @copyright   (C) 2015 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

extract($displayData);

/**
 * Layout variables
 * -----------------
 * @var  PlgSystemStats             $plugin        Plugin rendering this
layout
 * @var  \Joomla\Registry\Registry  $pluginParams  Plugin parameters
 * @var  array                      $statsData     Array containing the
data that will be sent to the stats server
 */
?>
<div class="alert alert-info js-pstats-alert"
style="display:none;">
	<button data-dismiss="alert" class="close"
type="button">×</button>
	<h2><?php echo
JText::_('PLG_SYSTEM_STATS_LABEL_MESSAGE_TITLE');
?></h2>
	<p>
		<?php echo
JText::_('PLG_SYSTEM_STATS_MSG_JOOMLA_WANTS_TO_SEND_DATA'); ?>
		<a href="#" class="js-pstats-btn-details
alert-link"><?php echo
JText::_('PLG_SYSTEM_STATS_MSG_WHAT_DATA_WILL_BE_SENT');
?></a>
	</p>
	<?php
		echo $plugin->render('stats',
compact('statsData'));
	?>
	<p><?php echo
JText::_('PLG_SYSTEM_STATS_MSG_ALLOW_SENDING_DATA');
?></p>
	<p class="actions">
		<a href="#" class="btn
js-pstats-btn-allow-always"><?php echo
JText::_('PLG_SYSTEM_STATS_BTN_SEND_ALWAYS'); ?></a>
		<a href="#" class="btn
js-pstats-btn-allow-once"><?php echo
JText::_('PLG_SYSTEM_STATS_BTN_SEND_NOW'); ?></a>
		<a href="#" class="btn
js-pstats-btn-allow-never"><?php echo
JText::_('PLG_SYSTEM_STATS_BTN_NEVER_SEND'); ?></a>
	</p>
</div>
PK��[_�mlffstats/layouts/stats.phpnu�[���<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.stats
 *
 * @copyright   (C) 2016 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

extract($displayData);

/**
 * Layout variables
 * -----------------
 * @var  array  $statsData  Array containing the data that will be sent to
the stats server
 */

$versionFields = array('php_version', 'db_version',
'cms_version');
?>
<dl class="dl-horizontal js-pstats-data-details" 
style="display:none;">
	<?php foreach ($statsData as $key => $value) : ?>
		<dt><?php echo JText::_('PLG_SYSTEM_STATS_LABEL_' .
strtoupper($key)); ?></dt>
		<dd><?php echo in_array($key, $versionFields) ?
(preg_match('/\d+(?:\.\d+)+/', $value, $matches) ? $matches[0] :
$value) : $value; ?></dd>
	<?php endforeach; ?>
</dl>
PK��[vc1��1�1stats/stats.phpnu�[���<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.stats
 *
 * @copyright   (C) 2015 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

// Uncomment the following line to enable debug mode for testing purposes.
Note: statistics will be sent on every page load
// define('PLG_SYSTEM_STATS_DEBUG', 1);

/**
 * Statistics system plugin. This sends anonymous data back to the Joomla!
Project about the
 * PHP, SQL, Joomla and OS versions
 *
 * @since  3.5
 */
class PlgSystemStats extends JPlugin
{
	/**
	 * Indicates sending statistics is always allowed.
	 *
	 * @var    integer
	 * @since  3.5
	 */
	const MODE_ALLOW_ALWAYS = 1;

	/**
	 * Indicates sending statistics is only allowed one time.
	 *
	 * @var    integer
	 * @since  3.5
	 */
	const MODE_ALLOW_ONCE = 2;

	/**
	 * Indicates sending statistics is never allowed.
	 *
	 * @var    integer
	 * @since  3.5
	 */
	const MODE_ALLOW_NEVER = 3;

	/**
	 * Application object
	 *
	 * @var    JApplicationCms
	 * @since  3.5
	 */
	protected $app;

	/**
	 * Database object
	 *
	 * @var    JDatabaseDriver
	 * @since  3.5
	 */
	protected $db;

	/**
	 * URL to send the statistics.
	 *
	 * @var    string
	 * @since  3.5
	 */
	protected $serverUrl =
'https://developer.joomla.org/stats/submit';

	/**
	 * Unique identifier for this site
	 *
	 * @var    string
	 * @since  3.5
	 */
	protected $uniqueId;

	/**
	 * Listener for the `onAfterInitialise` event
	 *
	 * @return  void
	 *
	 * @since   3.5
	 */
	public function onAfterInitialise()
	{
		if (!$this->app->isClient('administrator') ||
!$this->isAllowedUser())
		{
			return;
		}

		if (!$this->isDebugEnabled() && !$this->isUpdateRequired())
		{
			return;
		}

		if (JUri::getInstance()->getVar('tmpl') ===
'component')
		{
			return;
		}

		// Load plugin language files only when needed (ex: they are not needed
in site client).
		$this->loadLanguage();

		JHtml::_('jquery.framework');
		JHtml::_('script', 'plg_system_stats/stats.js',
array('version' => 'auto', 'relative'
=> true));
	}

	/**
	 * User selected to always send data
	 *
	 * @return  void
	 *
	 * @since   3.5
	 *
	 * @throws  Exception         If user is not allowed.
	 * @throws  RuntimeException  If there is an error saving the params or
sending the data.
	 */
	public function onAjaxSendAlways()
	{
		if (!$this->isAllowedUser() || !$this->isAjaxRequest())
		{
			throw new Exception(JText::_('JGLOBAL_AUTH_ACCESS_DENIED'),
403);
		}

		$this->params->set('mode', static::MODE_ALLOW_ALWAYS);

		if (!$this->saveParams())
		{
			throw new RuntimeException('Unable to save plugin settings',
500);
		}

		$this->sendStats();

		echo json_encode(array('sent' => 1));
	}

	/**
	 * User selected to never send data.
	 *
	 * @return  void
	 *
	 * @since   3.5
	 *
	 * @throws  Exception         If user is not allowed.
	 * @throws  RuntimeException  If there is an error saving the params.
	 */
	public function onAjaxSendNever()
	{
		if (!$this->isAllowedUser() || !$this->isAjaxRequest())
		{
			throw new Exception(JText::_('JGLOBAL_AUTH_ACCESS_DENIED'),
403);
		}

		$this->params->set('mode', static::MODE_ALLOW_NEVER);

		if (!$this->saveParams())
		{
			throw new RuntimeException('Unable to save plugin settings',
500);
		}

		echo json_encode(array('sent' => 0));
	}

	/**
	 * User selected to send data once.
	 *
	 * @return  void
	 *
	 * @since   3.5
	 *
	 * @throws  Exception         If user is not allowed.
	 * @throws  RuntimeException  If there is an error saving the params or
sending the data.
	 */
	public function onAjaxSendOnce()
	{
		if (!$this->isAllowedUser() || !$this->isAjaxRequest())
		{
			throw new Exception(JText::_('JGLOBAL_AUTH_ACCESS_DENIED'),
403);
		}

		$this->params->set('mode', static::MODE_ALLOW_ONCE);

		if (!$this->saveParams())
		{
			throw new RuntimeException('Unable to save plugin settings',
500);
		}

		$this->sendStats();

		echo json_encode(array('sent' => 1));
	}

	/**
	 * Send the stats to the server.
	 * On first load | on demand mode it will show a message asking users to
select mode.
	 *
	 * @return  void
	 *
	 * @since   3.5
	 *
	 * @throws  Exception         If user is not allowed.
	 * @throws  RuntimeException  If there is an error saving the params or
sending the data.
	 */
	public function onAjaxSendStats()
	{
		if (!$this->isAllowedUser() || !$this->isAjaxRequest())
		{
			throw new Exception(JText::_('JGLOBAL_AUTH_ACCESS_DENIED'),
403);
		}

		// User has not selected the mode. Show message.
		if ((int) $this->params->get('mode') !==
static::MODE_ALLOW_ALWAYS)
		{
			$data = array(
				'sent' => 0,
				'html' =>
$this->getRenderer('message')->render($this->getLayoutData())
			);

			echo json_encode($data);

			return;
		}

		if (!$this->saveParams())
		{
			throw new RuntimeException('Unable to save plugin settings',
500);
		}

		$this->sendStats();

		echo json_encode(array('sent' => 1));
	}

	/**
	 * Get the data through events
	 *
	 * @param   string  $context  Context where this will be called from
	 *
	 * @return  array
	 *
	 * @since   3.5
	 */
	public function onGetStatsData($context)
	{
		return $this->getStatsData();
	}

	/**
	 * Debug a layout of this plugin
	 *
	 * @param   string  $layoutId  Layout identifier
	 * @param   array   $data      Optional data for the layout
	 *
	 * @return  string
	 *
	 * @since   3.5
	 */
	public function debug($layoutId, $data = array())
	{
		$data = array_merge($this->getLayoutData(), $data);

		return $this->getRenderer($layoutId)->debug($data);
	}

	/**
	 * Get the data for the layout
	 *
	 * @return  array
	 *
	 * @since   3.5
	 */
	protected function getLayoutData()
	{
		return array(
			'plugin'       => $this,
			'pluginParams' => $this->params,
			'statsData'    => $this->getStatsData()
		);
	}

	/**
	 * Get the layout paths
	 *
	 * @return  array
	 *
	 * @since   3.5
	 */
	protected function getLayoutPaths()
	{
		$template = JFactory::getApplication()->getTemplate();

		return array(
			JPATH_ADMINISTRATOR . '/templates/' . $template .
'/html/layouts/plugins/' . $this->_type . '/' .
$this->_name,
			__DIR__ . '/layouts',
		);
	}

	/**
	 * Get the plugin renderer
	 *
	 * @param   string  $layoutId  Layout identifier
	 *
	 * @return  JLayout
	 *
	 * @since   3.5
	 */
	protected function getRenderer($layoutId = 'default')
	{
		$renderer = new JLayoutFile($layoutId);

		$renderer->setIncludePaths($this->getLayoutPaths());

		return $renderer;
	}

	/**
	 * Get the data that will be sent to the stats server.
	 *
	 * @return  array
	 *
	 * @since   3.5
	 */
	private function getStatsData()
	{
		$data = array(
			'unique_id'   => $this->getUniqueId(),
			'php_version' => PHP_VERSION,
			'db_type'     => $this->db->name,
			'db_version'  => $this->db->getVersion(),
			'cms_version' => JVERSION,
			'server_os'   => php_uname('s') . ' ' .
php_uname('r')
		);

		// Check if we have a MariaDB version string and extract the proper
version from it
		if
(preg_match('/^(?:5\.5\.5-)?(mariadb-)?(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)/i',
$data['db_version'], $versionParts))
		{
			$data['db_version'] = $versionParts['major'] .
'.' . $versionParts['minor'] . '.' .
$versionParts['patch'];
		}

		return $data;
	}

	/**
	 * Get the unique id. Generates one if none is set.
	 *
	 * @return  integer
	 *
	 * @since   3.5
	 */
	private function getUniqueId()
	{
		if (null === $this->uniqueId)
		{
			$this->uniqueId = $this->params->get('unique_id',
hash('sha1', JUserHelper::genRandomPassword(28) . time()));
		}

		return $this->uniqueId;
	}

	/**
	 * Check if current user is allowed to send the data
	 *
	 * @return  boolean
	 *
	 * @since   3.5
	 */
	private function isAllowedUser()
	{
		return JFactory::getUser()->authorise('core.admin');
	}

	/**
	 * Check if the debug is enabled
	 *
	 * @return  boolean
	 *
	 * @since   3.5
	 */
	private function isDebugEnabled()
	{
		return defined('PLG_SYSTEM_STATS_DEBUG');
	}

	/**
	 * Check if last_run + interval > now
	 *
	 * @return  boolean
	 *
	 * @since   3.5
	 */
	private function isUpdateRequired()
	{
		$last     = (int) $this->params->get('lastrun', 0);
		$interval = (int) $this->params->get('interval', 12);
		$mode     = (int) $this->params->get('mode', 0);

		if ($mode === static::MODE_ALLOW_NEVER)
		{
			return false;
		}

		// Never updated or debug enabled
		if (!$last || $this->isDebugEnabled())
		{
			return true;
		}

		return (abs(time() - $last) > $interval * 3600);
	}

	/**
	 * Check valid AJAX request
	 *
	 * @return  boolean
	 *
	 * @since   3.5
	 */
	private function isAjaxRequest()
	{
		return
strtolower($this->app->input->server->get('HTTP_X_REQUESTED_WITH',
'')) === 'xmlhttprequest';
	}

	/**
	 * Render a layout of this plugin
	 *
	 * @param   string  $layoutId  Layout identifier
	 * @param   array   $data      Optional data for the layout
	 *
	 * @return  string
	 *
	 * @since   3.5
	 */
	public function render($layoutId, $data = array())
	{
		$data = array_merge($this->getLayoutData(), $data);

		return $this->getRenderer($layoutId)->render($data);
	}

	/**
	 * Save the plugin parameters
	 *
	 * @return  boolean
	 *
	 * @since   3.5
	 */
	private function saveParams()
	{
		// Update params
		$this->params->set('lastrun', time());
		$this->params->set('unique_id', $this->getUniqueId());
		$interval = (int) $this->params->get('interval', 12);
		$this->params->set('interval', $interval ?: 12);

		$query = $this->db->getQuery(true)
				->update($this->db->quoteName('#__extensions'))
				->set($this->db->quoteName('params') . ' =
' .
$this->db->quote($this->params->toString('JSON')))
				->where($this->db->quoteName('type') . ' =
' . $this->db->quote('plugin'))
				->where($this->db->quoteName('folder') . ' =
' . $this->db->quote('system'))
				->where($this->db->quoteName('element') . ' =
' . $this->db->quote('stats'));

		try
		{
			// Lock the tables to prevent multiple plugin executions causing a race
condition
			$this->db->lockTable('#__extensions');
		}
		catch (Exception $e)
		{
			// If we can't lock the tables it's too risky to continue
execution
			return false;
		}

		try
		{
			// Update the plugin parameters
			$result = $this->db->setQuery($query)->execute();

			$this->clearCacheGroups(array('com_plugins'), array(0, 1));
		}
		catch (Exception $exc)
		{
			// If we failed to execute
			$this->db->unlockTables();
			$result = false;
		}

		try
		{
			// Unlock the tables after writing
			$this->db->unlockTables();
		}
		catch (Exception $e)
		{
			// If we can't lock the tables assume we have somehow failed
			$result = false;
		}

		return $result;
	}

	/**
	 * Send the stats to the stats server
	 *
	 * @return  boolean
	 *
	 * @since   3.5
	 *
	 * @throws  RuntimeException  If there is an error sending the data.
	 */
	private function sendStats()
	{
		try
		{
			// Don't let the request take longer than 2 seconds to avoid page
timeout issues
			$response = JHttpFactory::getHttp()->post($this->serverUrl,
$this->getStatsData(), null, 2);
		}
		catch (UnexpectedValueException $e)
		{
			// There was an error sending stats. Should we do anything?
			throw new RuntimeException('Could not send site statistics to
remote server: ' . $e->getMessage(), 500);
		}
		catch (RuntimeException $e)
		{
			// There was an error connecting to the server or in the post request
			throw new RuntimeException('Could not connect to statistics server:
' . $e->getMessage(), 500);
		}
		catch (Exception $e)
		{
			// An unexpected error in processing; don't let this failure kill
the site
			throw new RuntimeException('Unexpected error connecting to
statistics server: ' . $e->getMessage(), 500);
		}

		if ($response->code !== 200)
		{
			$data = json_decode($response->body);

			throw new RuntimeException('Could not send site statistics to
remote server: ' . $data->message, $response->code);
		}

		return true;
	}

	/**
	 * Clears cache groups. We use it to clear the plugins cache after we
update the last run timestamp.
	 *
	 * @param   array  $clearGroups   The cache groups to clean
	 * @param   array  $cacheClients  The cache clients (site, admin) to clean
	 *
	 * @return  void
	 *
	 * @since   3.5
	 */
	private function clearCacheGroups(array $clearGroups, array $cacheClients
= array(0, 1))
	{
		foreach ($clearGroups as $group)
		{
			foreach ($cacheClients as $client_id)
			{
				try
				{
					$options = array(
						'defaultgroup' => $group,
						'cachebase'    => $client_id ? JPATH_ADMINISTRATOR .
'/cache' : $this->app->get('cache_path',
JPATH_SITE . '/cache')
					);

					$cache = JCache::getInstance('callback', $options);
					$cache->clean();
				}
				catch (Exception $e)
				{
					// Ignore it
				}
			}
		}
	}
}
PK��[�+Nbbstats/stats.xmlnu�[���<?xml
version="1.0" encoding="utf-8"?>
<extension version="3.6" type="plugin"
group="system" method="upgrade">
	<name>plg_system_stats</name>
	<author>Joomla! Project</author>
	<creationDate>November 2013</creationDate>
	<copyright>(C) 2013 Open Source Matters, Inc.</copyright>
	<license>GNU General Public License version 2 or later; see
LICENSE.txt</license>
	<authorEmail>admin@joomla.org</authorEmail>
	<authorUrl>www.joomla.org</authorUrl>
	<version>3.5.0</version>
	<description>PLG_SYSTEM_STATS_XML_DESCRIPTION</description>
	<files>
		<folder>field</folder>
		<folder>layouts</folder>
		<filename plugin="stats">stats.php</filename>
	</files>
	<languages folder="language">
		<language
tag="en-GB">en-GB/en-GB.plg_system_stats.ini</language>
		<language
tag="en-GB">en-GB/en-GB.plg_system_stats.sys.ini</language>
	</languages>
	<config>
		<fields name="params">
			<fieldset name="basic">
				<field
					name="data"
					type="plgsystemstats.data"
					label=""
				/>

				<field
					name="unique_id"
					type="plgsystemstats.uniqueid"
					label="PLG_SYSTEM_STATS_UNIQUE_ID_LABEL"
					description="PLG_SYSTEM_STATS_UNIQUE_ID_DESC"
					size="10"
				/>

				<field
					name="interval"
					type="number"
					label="PLG_SYSTEM_STATS_INTERVAL_LABEL"
					description="PLG_SYSTEM_STATS_INTERVAL_DESC"
					filter="integer"
					default="12"
				/>

				<field
					name="mode"
					type="list"
					label="PLG_SYSTEM_STATS_MODE_LABEL"
					description="PLG_SYSTEM_STATS_MODE_DESC"
					default="1"
					>
					<option
value="1">PLG_SYSTEM_STATS_MODE_OPTION_ALWAYS_SEND</option>
					<option
value="2">PLG_SYSTEM_STATS_MODE_OPTION_ON_DEMAND</option>
					<option
value="3">PLG_SYSTEM_STATS_MODE_OPTION_NEVER_SEND</option>
				</field>

				<field
					name="lastrun"
					type="hidden"
					default="0"
					size="15"
				/>
			</fieldset>
		</fields>
	</config>
</extension>
PK��[�����2updatenotification/postinstall/updatecachetime.phpnu�[���<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.updatenotification
 *
 * @copyright   (C) 2016 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

/**
 * Checks if the com_installer config for the cache Hours are eq 0 and the
updatenotification Plugin is enabled
 *
 * @return  boolean
 *
 * @since   3.6.3
 */
function updatecachetime_postinstall_condition()
{
	$cacheTimeout = (int)
JComponentHelper::getComponent('com_installer')->params->get('cachetimeout',
6);

	// Check if cachetimeout is eq zero
	if ($cacheTimeout === 0 &&
JPluginHelper::isEnabled('system',
'updatenotification'))
	{
		return true;
	}

	return false;
}

/**
 * Sets the cachetimeout back to the default (6 hours)
 *
 * @return  void
 *
 * @since   3.6.3
 */
function updatecachetime_postinstall_action()
{
	$installer = JComponentHelper::getComponent('com_installer');

	// Sets the cachetimeout back to the default (6 hours)
	$installer->params->set('cachetimeout', 6);

	// Save the new parameters back to com_installer
	$table = JTable::getInstance('extension');
	$table->load($installer->id);
	$table->bind(array('params' =>
$installer->params->toString()));

	// Store the changes
	if (!$table->store())
	{
		// If there is an error show it to the admin
		JFactory::getApplication()->enqueueMessage($table->getError(),
'error');
	}
}
PK��[��&�V-V-)updatenotification/updatenotification.phpnu�[���<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.updatenotification
 *
 * @copyright   (C) 2015 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

// Uncomment the following line to enable debug mode (update notification
email sent every single time)
// define('PLG_SYSTEM_UPDATENOTIFICATION_DEBUG', 1);

/**
 * Joomla! Update Notification plugin
 *
 * Sends out an email to all Super Users or a predefined email address when
a new Joomla! version is available.
 *
 * This plugin is a direct adaptation of the corresponding plugin in Akeeba
Ltd's Admin Tools. The author has
 * consented to relicensing their plugin's code under GPLv2 or later
(the original version was licensed under
 * GPLv3 or later) to allow its inclusion in the Joomla! CMS.
 *
 * @since  3.5
 */
class PlgSystemUpdatenotification extends JPlugin
{
	/**
	 * Load plugin language files automatically
	 *
	 * @var    boolean
	 * @since  3.6.3
	 */
	protected $autoloadLanguage = true;

	/**
	 * The update check and notification email code is triggered after the
page has fully rendered.
	 *
	 * @return  void
	 *
	 * @since   3.5
	 */
	public function onAfterRender()
	{
		// Get the timeout for Joomla! updates, as configured in
com_installer's component parameters
		$component = JComponentHelper::getComponent('com_installer');

		/** @var \Joomla\Registry\Registry $params */
		$params        = $component->params;
		$cache_timeout = (int) $params->get('cachetimeout', 6);
		$cache_timeout = 3600 * $cache_timeout;

		// Do we need to run? Compare the last run timestamp stored in the
plugin's options with the current
		// timestamp. If the difference is greater than the cache timeout we
shall not execute again.
		$now  = time();
		$last = (int) $this->params->get('lastrun', 0);

		if (!defined('PLG_SYSTEM_UPDATENOTIFICATION_DEBUG') &&
(abs($now - $last) < $cache_timeout))
		{
			return;
		}

		// Update last run status
		// If I have the time of the last run, I can update, otherwise insert
		$this->params->set('lastrun', $now);

		$db = JFactory::getDbo();
		$query = $db->getQuery(true)
					->update($db->qn('#__extensions'))
					->set($db->qn('params') . ' = ' .
$db->q($this->params->toString('JSON')))
					->where($db->qn('type') . ' = ' .
$db->q('plugin'))
					->where($db->qn('folder') . ' = ' .
$db->q('system'))
					->where($db->qn('element') . ' = ' .
$db->q('updatenotification'));

		try
		{
			// Lock the tables to prevent multiple plugin executions causing a race
condition
			$db->lockTable('#__extensions');
		}
		catch (Exception $e)
		{
			// If we can't lock the tables it's too risky to continue
execution
			return;
		}

		try
		{
			// Update the plugin parameters
			$result = $db->setQuery($query)->execute();

			$this->clearCacheGroups(array('com_plugins'), array(0, 1));
		}
		catch (Exception $exc)
		{
			// If we failed to execute
			$db->unlockTables();
			$result = false;
		}

		try
		{
			// Unlock the tables after writing
			$db->unlockTables();
		}
		catch (Exception $e)
		{
			// If we can't lock the tables assume we have somehow failed
			$result = false;
		}

		// Abort on failure
		if (!$result)
		{
			return;
		}

		// This is the extension ID for Joomla! itself
		$eid = 700;

		// Get any available updates
		$updater = JUpdater::getInstance();
		$results = $updater->findUpdates(array($eid), $cache_timeout);

		// If there are no updates our job is done. We need BOTH this check AND
the one below.
		if (!$results)
		{
			return;
		}

		// Unfortunately Joomla! MVC doesn't allow us to autoload classes
		JModelLegacy::addIncludePath(JPATH_ADMINISTRATOR .
'/components/com_installer/models', 'InstallerModel');

		// Get the update model and retrieve the Joomla! core updates
		$model = JModelLegacy::getInstance('Update',
'InstallerModel');
		$model->setState('filter.extension_id', $eid);
		$updates = $model->getItems();

		// If there are no updates we don't have to notify anyone about
anything. This is NOT a duplicate check.
		if (empty($updates))
		{
			return;
		}

		// Get the available update
		$update = array_pop($updates);

		// Check the available version. If it's the same or less than the
installed version we have no updates to notify about.
		if (version_compare($update->version, JVERSION, 'le'))
		{
			return;
		}

		// If we're here, we have updates. First, get a link to the Joomla!
Update component.
		$baseURL  = JUri::base();
		$baseURL  = rtrim($baseURL, '/');
		$baseURL .= (substr($baseURL, -13) !== 'administrator') ?
'/administrator/' : '/';
		$baseURL .= 'index.php?option=com_joomlaupdate';
		$uri      = new JUri($baseURL);

		/**
		 * Some third party security solutions require a secret query parameter
to allow log in to the administrator
		 * backend of the site. The link generated above will be invalid and
could probably block the user out of their
		 * site, confusing them (they can't understand the third party
security solution is not part of Joomla! proper).
		 * So, we're calling the onBuildAdministratorLoginURL system plugin
event to let these third party solutions
		 * add any necessary secret query parameters to the URL. The plugins are
supposed to have a method with the
		 * signature:
		 *
		 * public function onBuildAdministratorLoginURL(JUri &$uri);
		 *
		 * The plugins should modify the $uri object directly and return null.
		 */

		JEventDispatcher::getInstance()->trigger('onBuildAdministratorLoginURL',
array(&$uri));

		// Let's find out the email addresses to notify
		$superUsers    = array();
		$specificEmail = $this->params->get('email',
'');

		if (!empty($specificEmail))
		{
			$superUsers = $this->getSuperUsers($specificEmail);
		}

		if (empty($superUsers))
		{
			$superUsers = $this->getSuperUsers();
		}

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

		/*
		 * Load the appropriate language. We try to load English (UK), the
current user's language and the forced
		 * language preference, in this order. This ensures that we'll never
end up with untranslated strings in the
		 * update email which would make Joomla! seem bad. So, please, if you
don't fully understand what the
		 * following code does DO NOT TOUCH IT. It makes the difference between a
hobbyist CMS and a professional
		 * solution! 
		 */
		$jLanguage = JFactory::getLanguage();
		$jLanguage->load('plg_system_updatenotification',
JPATH_ADMINISTRATOR, 'en-GB', true, true);
		$jLanguage->load('plg_system_updatenotification',
JPATH_ADMINISTRATOR, null, true, false);

		// Then try loading the preferred (forced) language
		$forcedLanguage = $this->params->get('language_override',
'');

		if (!empty($forcedLanguage))
		{
			$jLanguage->load('plg_system_updatenotification',
JPATH_ADMINISTRATOR, $forcedLanguage, true, false);
		}

		// Set up the email subject and body

		$email_subject =
JText::_('PLG_SYSTEM_UPDATENOTIFICATION_EMAIL_SUBJECT');
		$email_body    =
JText::_('PLG_SYSTEM_UPDATENOTIFICATION_EMAIL_BODY');

		// Replace merge codes with their values
		$newVersion = $update->version;

		$jVersion       = new JVersion;
		$currentVersion = $jVersion->getShortVersion();

		$jConfig  = JFactory::getConfig();
		$sitename = $jConfig->get('sitename');
		$mailFrom = $jConfig->get('mailfrom');
		$fromName = $jConfig->get('fromname');

		$substitutions = array(
			'[NEWVERSION]'  => $newVersion,
			'[CURVERSION]'  => $currentVersion,
			'[SITENAME]'    => $sitename,
			'[URL]'         => JUri::base(),
			'[LINK]'        => $uri->toString(),
			'[RELEASENEWS]' =>
'https://www.joomla.org/announcements/release-news/',
			'\\n'           => "\n",
		);

		foreach ($substitutions as $k => $v)
		{
			$email_subject = str_replace($k, $v, $email_subject);
			$email_body    = str_replace($k, $v, $email_body);
		}

		// Send the emails to the Super Users
		foreach ($superUsers as $superUser)
		{
			$mailer = JFactory::getMailer();
			$mailer->setSender(array($mailFrom, $fromName));
			$mailer->addRecipient($superUser->email);
			$mailer->setSubject($email_subject);
			$mailer->setBody($email_body);
			$mailer->Send();
		}
	}

	/**
	 * Returns the Super Users email information. If you provide a comma
separated $email list
	 * we will check that these emails do belong to Super Users and that they
have not blocked
	 * system emails.
	 *
	 * @param   null|string  $email  A list of Super Users to email
	 *
	 * @return  array  The list of Super User emails
	 *
	 * @since   3.5
	 */
	private function getSuperUsers($email = null)
	{
		// Get a reference to the database object
		$db = JFactory::getDbo();

		// Convert the email list to an array
		if (!empty($email))
		{
			$temp   = explode(',', $email);
			$emails = array();

			foreach ($temp as $entry)
			{
				$entry    = trim($entry);
				$emails[] = $db->q($entry);
			}

			$emails = array_unique($emails);
		}
		else
		{
			$emails = array();
		}

		// Get a list of groups which have Super User privileges
		$ret = array();

		try
		{
			$rootId    = JTable::getInstance('Asset',
'JTable')->getRootId();
			$rules     = JAccess::getAssetRules($rootId)->getData();
			$rawGroups = $rules['core.admin']->getData();
			$groups    = array();

			if (empty($rawGroups))
			{
				return $ret;
			}

			foreach ($rawGroups as $g => $enabled)
			{
				if ($enabled)
				{
					$groups[] = $db->q($g);
				}
			}

			if (empty($groups))
			{
				return $ret;
			}
		}
		catch (Exception $exc)
		{
			return $ret;
		}

		// Get the user IDs of users belonging to the SA groups
		try
		{
			$query = $db->getQuery(true)
						->select($db->qn('user_id'))
						->from($db->qn('#__user_usergroup_map'))
						->where($db->qn('group_id') . ' IN(' .
implode(',', $groups) . ')');
			$db->setQuery($query);
			$rawUserIDs = $db->loadColumn(0);

			if (empty($rawUserIDs))
			{
				return $ret;
			}

			$userIDs = array();

			foreach ($rawUserIDs as $id)
			{
				$userIDs[] = $db->q($id);
			}
		}
		catch (Exception $exc)
		{
			return $ret;
		}

		// Get the user information for the Super Administrator users
		try
		{
			$query = $db->getQuery(true)
						->select(
							array(
								$db->qn('id'),
								$db->qn('username'),
								$db->qn('email'),
							)
						)->from($db->qn('#__users'))
						->where($db->qn('id') . ' IN(' .
implode(',', $userIDs) . ')')
						->where($db->qn('block') . ' = 0')
						->where($db->qn('sendEmail') . ' = ' .
$db->q('1'));

			if (!empty($emails))
			{
				$query->where('LOWER(' . $db->qn('email') .
') IN(' . implode(',',
array_map('strtolower', $emails)) . ')');
			}

			$db->setQuery($query);
			$ret = $db->loadObjectList();
		}
		catch (Exception $exc)
		{
			return $ret;
		}

		return $ret;
	}

	/**
	 * Clears cache groups. We use it to clear the plugins cache after we
update the last run timestamp.
	 *
	 * @param   array  $clearGroups   The cache groups to clean
	 * @param   array  $cacheClients  The cache clients (site, admin) to clean
	 *
	 * @return  void
	 *
	 * @since   3.5
	 */
	private function clearCacheGroups(array $clearGroups, array $cacheClients
= array(0, 1))
	{
		$conf = JFactory::getConfig();

		foreach ($clearGroups as $group)
		{
			foreach ($cacheClients as $client_id)
			{
				try
				{
					$options = array(
						'defaultgroup' => $group,
						'cachebase'    => $client_id ? JPATH_ADMINISTRATOR .
'/cache' :
							$conf->get('cache_path', JPATH_SITE .
'/cache')
					);

					$cache = JCache::getInstance('callback', $options);
					$cache->clean();
				}
				catch (Exception $e)
				{
					// Ignore it
				}
			}
		}
	}
}
PK��[�Z::)updatenotification/updatenotification.xmlnu�[���<?xml
version="1.0" encoding="utf-8"?>
<extension version="3.6" type="plugin"
group="system" method="upgrade">
	<name>plg_system_updatenotification</name>
	<author>Joomla! Project</author>
	<creationDate>May 2015</creationDate>
	<copyright>(C) 2015 Open Source Matters, Inc.</copyright>
	<license>GNU General Public License version 2 or later; see
LICENSE.txt</license>
	<authorEmail>admin@joomla.org</authorEmail>
	<authorUrl>www.joomla.org</authorUrl>
	<version>3.5.0</version>
	<description>PLG_SYSTEM_UPDATENOTIFICATION_XML_DESCRIPTION</description>
	<files>
		<filename
plugin="updatenotification">updatenotification.php</filename>
	</files>
	<languages folder="language">
		<language
tag="en-GB">en-GB.plg_system_updatenotification.ini</language>
		<language
tag="en-GB">en-GB.plg_system_updatenotification.sys.ini</language>
	</languages>
	<config>
		<fields name="params">
			<fieldset name="basic">
				<field
					name="email"
					type="text"
					label="PLG_SYSTEM_UPDATENOTIFICATION_EMAIL_LBL"
					description="PLG_SYSTEM_UPDATENOTIFICATION_EMAIL_DESC"
					default=""
					size="40"
				/>

				<field
					name="language_override"
					type="language"
					label="PLG_SYSTEM_UPDATENOTIFICATION_LANGUAGE_OVERRIDE_LBL"
					description="PLG_SYSTEM_UPDATENOTIFICATION_LANGUAGE_OVERRIDE_DESC"
					default=""
					client="administrator"
					>
					<option
value="">PLG_SYSTEM_UPDATENOTIFICATION_LANGUAGE_OVERRIDE_NONE</option>
				</field>

				<field
					name="lastrun"
					type="hidden"
					default="0"
					size="15"
				/>
			</fieldset>
		</fields>
	</config>
</extension>
PKmE�[c
���message.phpnu�[���<?php
/**
 * @package     Joomla.Site
 * @subpackage  Layout
 *
 * @copyright   (C) 2014 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('_JEXEC') or die;

$msgList = $displayData['msgList'];

?>
<div id="system-message-container">
	<?php if (is_array($msgList) && !empty($msgList)) : ?>
		<div id="system-message">
			<?php foreach ($msgList as $type => $msgs) : ?>
				<div class="alert alert-<?php echo $type; ?>">
					<?php // This requires JS so we should add it through JS.
Progressive enhancement and stuff. ?>
					<a class="close"
data-dismiss="alert">×</a>

					<?php if (!empty($msgs)) : ?>
						<h4 class="alert-heading"><?php echo
JText::_($type); ?></h4>
						<div>
							<?php foreach ($msgs as $msg) : ?>
								<div class="alert-message"><?php echo $msg;
?></div>
							<?php endforeach; ?>
						</div>
					<?php endif; ?>
				</div>
			<?php endforeach; ?>
		</div>
	<?php endif; ?>
</div>
PKu�[��]��rating_star.pngnu�[���PKu�[W9����rating_star_blank.pngnu�[���PKu�[��V\��sort_asc.pngnu�[���PKu�[J!G`��
�sort_desc.pngnu�[���PK|��[���/�/�actionlogs/actionlogs.phpnu�[���PK|��[؞����3actionlogs/actionlogs.xmlnu�[���PK|��[��<__�8actionlogs/forms/actionlogs.xmlnu�[���PK|��[�����
t<actionlogs/forms/information.xmlnu�[���PK|��[�~�ccS>cache/cache.phpnu�[���PK|��[a6κ���Tcache/cache.xmlnu�[���PK|��[�+Gb�b��[debug/debug.phpnu�[���PK|��[�i�>>bdebug/debug.xmlnu�[���PK|��[��W8�2�2�=fields/fields.phpnu�[���PK|��[W���qfields/fields.xmlnu�[���PK|��[#�K<<dthighlight/highlight.phpnu�[���PK|��[;��f44�|highlight/highlight.xmlnu�[���PK|��[�����=b�languagecode/language/en-GB/en-GB.plg_system_languagecode.ininu�[���PK|��[o���Ap�languagecode/language/en-GB/en-GB.plg_system_languagecode.sys.ininu�[���PK|��[�}`c�languagecode/languagecode.phpnu�[���PK|��[�x�.����languagecode/languagecode.xmlnu�[���PK|��[�ա�b�b!��languagefilter/languagefilter.phpnu�[���PK|��[aVOuss!�languagefilter/languagefilter.xmlnu�[���PK}��[~����
log/log.phpnu�[���PK}��[�$K���log/log.xmlnu�[���PK}��[[�G%�
�
�logout/logout.phpnu�[���PK}��[��C��#logout/logout.xmlnu�[���PK}��[X�/9���&logrotation/logrotation.phpnu�[���PK}��[�g�##�?logrotation/logrotation.xmlnu�[���PK}��[�g��9Fp3p/p3p.phpnu�[���PK}��[�!$$�Ip3p/p3p.xmlnu�[���PK}��[��ɢ�
�
 [Nprivacyconsent/field/privacy.phpnu�[���PK}��[�h=::0>Yprivacyconsent/privacyconsent/privacyconsent.xmlnu�[���PK}��[1���MM!�[privacyconsent/privacyconsent.phpnu�[���PK}��[�UqGJ
J
!/�privacyconsent/privacyconsent.xmlnu�[���PK}��[1�r��ʶredirect/form/excludes.xmlnu�[���PK}��[�6j�%&%&�redirect/redirect.phpnu�[���PK}��[-LS�TTT�redirect/redirect.xmlnu�[���PK}��[���
�
��remember/remember.phpnu�[���PK}��[�-����remember/remember.xmlnu�[���PK}��[?>�
���sef/sef.phpnu�[���PK}��[n*mf 
�sef/sef.xmlnu�[���PK}��[ҽ�F��$sessiongc/sessiongc.phpnu�[���PK}��[sPw��4
sessiongc/sessiongc.xmlnu�[���PK}��[�V���[)stats/field/base.phpnu�[���PK}��[G!E����,stats/field/data.phpnu�[���PK}��[EN8��h1stats/field/uniqueid.phpnu�[���PK��[�e�����4stats/layouts/field/data.phpnu�[���PK��[oT=		
�=stats/layouts/field/uniqueid.phpnu�[���PK��[֙Q	Gstats/layouts/message.phpnu�[���PK��[_�mlff^Mstats/layouts/stats.phpnu�[���PK��[vc1��1�1Qstats/stats.phpnu�[���PK��[�+Nbb�stats/stats.xmlnu�[���PK��[�����2��updatenotification/postinstall/updatecachetime.phpnu�[���PK��[��&�V-V-)��updatenotification/updatenotification.phpnu�[���PK��[�Z::)F�updatenotification/updatenotification.xmlnu�[���PKmE�[c
�����message.phpnu�[���PK88U	�