Файловый менеджер - Редактировать - /home/lmsyaran/public_html/khsh/system.zip
Назад
PK u�[��]� � 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ư���U IEND�B`�PK u�[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`�PK u�[��V\� � sort_asc.pngnu �[��� �PNG IHDR �|�l GIDATx��G@!��^�Wp���';� �7�K ��� �����U��6=ҢŎ4k�$�U��OT��A� x���H��� IEND�B`�PK u�[J!G`� � sort_desc.pngnu �[��� �PNG IHDR �|�l NIDATx�� 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 |��[�~�c c cache/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 ms</span> / <span class="label label-default">%.2f 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 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 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 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 MB</span>', $memoryUsed / 1048576), sprintf('<span class="label label-default">%.3f 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(' ', $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 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(' ', $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 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 (non-breaking spaces) for less tall tables displayed. $htmlTd = preg_replace('/([^;]) /', '\1 ', $htmlTd); // Displays warnings for "Using filesort": $htmlTdWithWarnings = str_replace( 'Using 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 />  \\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 �2 fields/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 |��[;��f4 4 highlight/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�� � A languagecode/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 |��[aVOus s ! 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=: : 0 privacyconsent/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���M M ! 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�%&