Spade

Mini Shell

Directory:~$ /proc/self/root/home/lmsyaran/www/pusher/
Upload File

[Home] [System Details] [Kill Me]
Current File:~$ //proc/self/root/home/lmsyaran/www/pusher/Helper.zip

PKlE�[���S��HelperFactoryAwareInterface.phpnu�[���<?php

/**
 * Joomla! Content Management System
 *
 * @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\CMS\Helper;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Interface to be implemented by classes depending on a helper factory.
 *
 * @since  4.2.0
 */
interface HelperFactoryAwareInterface
{
    /**
     * Sets the helper factory to use.
     *
     * @param   HelperFactory  $helper  The helper factory to use.
     *
     * @return  void
     *
     * @since   4.2.0
     */
    public function setHelperFactory(HelperFactory $helper);
}
PKlE�[���((LibraryHelper.phpnu�[���<?php

/**
 * Joomla! Content Management System
 *
 * @copyright  (C) 2013 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license    GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\CMS\Helper;

use Joomla\CMS\Cache\CacheControllerFactoryInterface;
use Joomla\CMS\Cache\Controller\CallbackController;
use Joomla\CMS\Cache\Exception\CacheExceptionInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Log\Log;
use Joomla\Database\DatabaseInterface;
use Joomla\Registry\Registry;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Library helper class
 *
 * @since  3.2
 */
class LibraryHelper
{
    /**
     * The component list cache
     *
     * @var    array
     * @since  3.2
     */
    protected static $libraries = [];

    /**
     * Get the library information.
     *
     * @param   string   $element  Element of the library in the extensions
table.
     * @param   boolean  $strict   If set and the library does not exist,
the enabled attribute will be set to false.
     *
     * @return  \stdClass   An object with the library's information.
     *
     * @since   3.2
     */
    public static function getLibrary($element, $strict = false)
    {
        // Is already cached?
        if (isset(static::$libraries[$element]) ||
static::loadLibrary($element)) {
            $result = static::$libraries[$element];

            // Convert the params to an object.
            if (\is_string($result->params)) {
                $result->params = new Registry($result->params);
            }
        } else {
            $result          = new \stdClass();
            $result->enabled = !$strict;
            $result->params  = new Registry();
        }

        return $result;
    }

    /**
     * Checks if a library is enabled
     *
     * @param   string  $element  Element of the library in the extensions
table.
     *
     * @return  boolean
     *
     * @since   3.2
     */
    public static function isEnabled($element)
    {
        return static::getLibrary($element, true)->enabled;
    }

    /**
     * Gets the parameter object for the library
     *
     * @param   string   $element  Element of the library in the extensions
table.
     * @param   boolean  $strict   If set and the library does not exist,
false will be returned
     *
     * @return  Registry  A Registry object.
     *
     * @see     Registry
     * @since   3.2
     */
    public static function getParams($element, $strict = false)
    {
        return static::getLibrary($element, $strict)->params;
    }

    /**
     * Save the parameters object for the library
     *
     * @param   string    $element  Element of the library in the
extensions table.
     * @param   Registry  $params   Params to save
     *
     * @return  Registry|boolean  A Registry object.
     *
     * @see     Registry
     * @since   3.2
     */
    public static function saveParams($element, $params)
    {
        if (static::isEnabled($element)) {
            // Save params in DB
            $db           =
Factory::getContainer()->get(DatabaseInterface::class);
            $paramsString = $params->toString();
            $query        = $db->getQuery(true)
                ->update($db->quoteName('#__extensions'))
                ->set($db->quoteName('params') . ' =
:params')
                ->where($db->quoteName('type') . ' =
' . $db->quote('library'))
                ->where($db->quoteName('element') . '
= :element')
                ->bind(':params', $paramsString)
                ->bind(':element', $element);
            $db->setQuery($query);

            $result = $db->execute();

            // Update params in libraries cache
            if ($result && isset(static::$libraries[$element])) {
                static::$libraries[$element]->params = $params;
            }

            return $result;
        }

        return false;
    }

    /**
     * Load the installed library into the libraries property.
     *
     * @param   string  $element  The element value for the extension
     *
     * @return  boolean  True on success
     *
     * @since   3.7.0
     */
    protected static function loadLibrary($element)
    {
        $loader = function ($element) {
            $db    =
Factory::getContainer()->get(DatabaseInterface::class);
            $query = $db->getQuery(true)
                ->select($db->quoteName(['extension_id',
'element', 'params', 'enabled'],
['id', 'option', null, null]))
                ->from($db->quoteName('#__extensions'))
                ->where($db->quoteName('type') . ' =
' . $db->quote('library'))
                ->where($db->quoteName('element') . '
= :element')
                ->bind(':element', $element);
            $db->setQuery($query);

            return $db->loadObject();
        };

        /** @var CallbackController $cache */
        $cache =
Factory::getContainer()->get(CacheControllerFactoryInterface::class)->createCacheController('callback',
['defaultgroup' => '_system']);

        try {
            static::$libraries[$element] = $cache->get($loader,
[$element], __METHOD__ . $element);
        } catch (CacheExceptionInterface $e) {
            static::$libraries[$element] = $loader($element);
        }

        if (empty(static::$libraries[$element])) {
            // Fatal error.
            $error =
Text::_('JLIB_APPLICATION_ERROR_LIBRARY_NOT_FOUND');
           
Log::add(Text::sprintf('JLIB_APPLICATION_ERROR_LIBRARY_NOT_LOADING',
$element, $error), Log::WARNING, 'jerror');

            return false;
        }

        return true;
    }
}
PKlE�[�o�3�3�TagsHelper.phpnu�[���<?php

/**
 * Joomla! Content Management System
 *
 * @copyright  (C) 2013 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license    GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\CMS\Helper;

use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Table\CoreContent;
use Joomla\CMS\Table\Table;
use Joomla\CMS\Table\TableInterface;
use Joomla\CMS\UCM\UCMContent;
use Joomla\CMS\UCM\UCMType;
use Joomla\Database\DatabaseInterface;
use Joomla\Database\ParameterType;
use Joomla\Utilities\ArrayHelper;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Tags helper class, provides methods to perform various tasks relevant
 * tagging of content.
 *
 * @since  3.1
 */
class TagsHelper extends CMSHelper
{
    /**
     * Helper object for storing and deleting tag information.
     *
     * @var    boolean
     * @since  3.1
     */
    protected $tagsChanged = false;

    /**
     * Whether up replace all tags or just add tags
     *
     * @var    boolean
     * @since  3.1
     */
    protected $replaceTags = false;

    /**
     * Alias for querying mapping and content type table.
     *
     * @var    string
     * @since  3.1
     */
    public $typeAlias;

    /**
     * Array of item tags.
     *
     * @var    array
     * @since  3.1
     */
    public $itemTags;

    /**
     * The tags as comma separated string or array.
     *
     * @var    mixed
     * @since  4.3.0
     */
    public $tags;

    /**
     * The new tags as comma separated string or array.
     *
     * @var    mixed
     * @since  4.3.0
     */
    public $newTags;

    /**
     * The old tags as comma separated string or array.
     *
     * @var    mixed
     * @since  4.3.0
     */
    public $oldTags;

    /**
     * Method to add tag rows to mapping table.
     *
     * @param   integer         $ucmId  ID of the #__ucm_content item being
tagged
     * @param   TableInterface  $table  Table object being tagged
     * @param   array           $tags   Array of tags to be applied.
     *
     * @return  boolean  true on success, otherwise false.
     *
     * @since   3.1
     */
    public function addTagMapping($ucmId, TableInterface $table, $tags =
[])
    {
        $db     = $table->getDbo();
        $key    = $table->getKeyName();
        $item   = $table->$key;
        $ucm    = new UCMType($this->typeAlias, $db);
        $typeId = $ucm->getTypeId();

        // Insert the new tag maps
        if (strpos(implode(',', $tags), '#') !== false)
{
            $tags = self::createTagsFromField($tags);
        }

        // Prevent saving duplicate tags
        $tags = array_values(array_unique($tags));

        if (!$tags) {
            return true;
        }

        $query = $db->getQuery(true);
        $query->insert('#__contentitem_tag_map');
        $query->columns(
            [
                $db->quoteName('core_content_id'),
                $db->quoteName('content_item_id'),
                $db->quoteName('tag_id'),
                $db->quoteName('type_id'),
                $db->quoteName('type_alias'),
                $db->quoteName('tag_date'),
            ]
        );

        foreach ($tags as $tag) {
            $query->values(
                implode(
                    ',',
                    array_merge(
                        $query->bindArray([(int) $ucmId, (int) $item,
(int) $tag, (int) $typeId]),
                        $query->bindArray([$this->typeAlias],
ParameterType::STRING),
                        [$query->currentTimestamp()]
                    )
                )
            );
        }

        $db->setQuery($query);

        return (bool) $db->execute();
    }

    /**
     * Function that converts tags paths into paths of names
     *
     * @param   array  $tags  Array of tags
     *
     * @return  array
     *
     * @since   3.1
     */
    public static function convertPathsToNames($tags)
    {
        // We will replace path aliases with tag names
        if ($tags) {
            // Create an array with all the aliases of the results
            $aliases = [];

            foreach ($tags as $tag) {
                if (!empty($tag->path)) {
                    if ($pathParts = explode('/', $tag->path))
{
                        $aliases = array_merge($aliases, $pathParts);
                    }
                }
            }

            // Get the aliases titles in one single query and map the
results
            if ($aliases) {
                // Remove duplicates
                $aliases = array_values(array_unique($aliases));

                $db = Factory::getDbo();

                $query = $db->getQuery(true)
                    ->select(
                        [
                            $db->quoteName('alias'),
                            $db->quoteName('title'),
                        ]
                    )
                    ->from($db->quoteName('#__tags'))
                    ->whereIn($db->quoteName('alias'),
$aliases, ParameterType::STRING);
                $db->setQuery($query);

                try {
                    $aliasesMapper =
$db->loadAssocList('alias');
                } catch (\RuntimeException $e) {
                    return false;
                }

                // Rebuild the items path
                if ($aliasesMapper) {
                    foreach ($tags as $tag) {
                        $namesPath = [];

                        if (!empty($tag->path)) {
                            if ($pathParts = explode('/',
$tag->path)) {
                                foreach ($pathParts as $alias) {
                                    if (isset($aliasesMapper[$alias])) {
                                        $namesPath[] =
$aliasesMapper[$alias]['title'];
                                    } else {
                                        $namesPath[] = $alias;
                                    }
                                }

                                $tag->text = implode('/',
$namesPath);
                            }
                        }
                    }
                }
            }
        }

        return $tags;
    }

    /**
     * Create any new tags by looking for #new# in the strings
     *
     * @param   array  $tags  Tags text array from the field
     *
     * @return  mixed   If successful, metadata with new tag titles
replaced by tag ids. Otherwise false.
     *
     * @since   3.1
     */
    public function createTagsFromField($tags)
    {
        if (empty($tags) || $tags[0] == '') {
            return;
        }

        // We will use the tags table to store them
        $tagTable  =
Factory::getApplication()->bootComponent('com_tags')->getMVCFactory()->createTable('Tag',
'Administrator');
        $newTags   = [];
        $canCreate =
Factory::getUser()->authorise('core.create',
'com_tags');

        foreach ($tags as $key => $tag) {
            // User is not allowed to create tags, so don't create.
            if (!$canCreate && strpos($tag, '#new#') !==
false) {
                continue;
            }

            // Remove the #new# prefix that identifies new tags
            $tagText = str_replace('#new#', '', $tag);

            if ($tagText === $tag) {
                $newTags[] = (int) $tag;
            } else {
                // Clear old data if exist
                $tagTable->reset();

                // Try to load the selected tag
                if ($tagTable->load(['title' => $tagText]))
{
                    $newTags[] = (int) $tagTable->id;
                } else {
                    // Prepare tag data
                    $tagTable->id          = 0;
                    $tagTable->title       = $tagText;
                    $tagTable->published   = 1;
                    $tagTable->description = '';

                    // $tagTable->language = property_exists ($item,
'language') ? $item->language : '*';
                    $tagTable->language = '*';
                    $tagTable->access   = 1;

                    // Make this item a child of the root tag
                    $tagTable->setLocation($tagTable->getRootId(),
'last-child');

                    // Try to store tag
                    if ($tagTable->check()) {
                        // Assign the alias as path (autogenerated tags
have always level 1)
                        $tagTable->path = $tagTable->alias;

                        if ($tagTable->store()) {
                            $newTags[] = (int) $tagTable->id;
                        }
                    }
                }
            }
        }

        // At this point $tags is an array of all tag ids
        $this->tags = $newTags;
        $result     = $newTags;

        return $result;
    }

    /**
     * Method to delete the tag mappings and #__ucm_content record for an
item
     *
     * @param   TableInterface  $table          Table object of content
table where delete occurred
     * @param   integer|array   $contentItemId  ID of the content item. Or
an array of key/value pairs with array key
     *                                          being a primary key name
and value being the content item ID. Note
     *                                          multiple primary keys are
not supported
     *
     * @return  boolean  true on success, false on failure
     *
     * @since   3.1
     * @throws  \InvalidArgumentException
     */
    public function deleteTagData(TableInterface $table, $contentItemId)
    {
        $key = $table->getKeyName();

        if (!\is_array($contentItemId)) {
            $contentItemId = [$key => $contentItemId];
        }

        // If we have multiple items for the content item primary key we
currently don't support this so
        // throw an InvalidArgumentException for now
        if (\count($contentItemId) != 1) {
            throw new \InvalidArgumentException('Multiple primary keys
are not supported as a content item id');
        }

        $result = $this->unTagItem($contentItemId[$key], $table);

        /** @var  CoreContent $ucmContentTable */
        $ucmContentTable = Table::getInstance('CoreContent');

        return $result &&
$ucmContentTable->deleteByContentId($contentItemId[$key],
$this->typeAlias);
    }

    /**
     * Method to get a list of tags for an item, optionally with the tag
data.
     *
     * @param   string   $contentType  Content type alias. Dot separated.
     * @param   integer  $id           Id of the item to retrieve tags for.
     * @param   boolean  $getTagData   If true, data from the tags table
will be included, defaults to true.
     *
     * @return  array    Array of tag objects
     *
     * @since   3.1
     */
    public function getItemTags($contentType, $id, $getTagData = true)
    {
        // Cast as integer until method is typehinted.
        $id = (int) $id;

        // Initialize some variables.
        $db    = Factory::getDbo();
        $query = $db->getQuery(true)
            ->select($db->quoteName('m.tag_id'))
            ->from($db->quoteName('#__contentitem_tag_map',
'm'))
            ->where(
                [
                    $db->quoteName('m.type_alias') . ' =
:contentType',
                    $db->quoteName('m.content_item_id') .
' = :id',
                    $db->quoteName('t.published') . ' =
1',
                ]
            )
            ->bind(':contentType', $contentType)
            ->bind(':id', $id, ParameterType::INTEGER);

        $user   = Factory::getUser();
        $groups = $user->getAuthorisedViewLevels();

        $query->whereIn($db->quoteName('t.access'),
$groups);

        // Optionally filter on language
        $language =
ComponentHelper::getParams('com_tags')->get('tag_list_language_filter',
'all');

        if ($language !== 'all') {
            if ($language === 'current_language') {
                $language = $this->getCurrentLanguage();
            }

            $query->whereIn($db->quoteName('language'),
[$language, '*'], ParameterType::STRING);
        }

        if ($getTagData) {
            $query->select($db->quoteName('t') .
'.*');
        }

        $query->join('INNER',
$db->quoteName('#__tags', 't'),
$db->quoteName('m.tag_id') . ' = ' .
$db->quoteName('t.id'));

        $db->setQuery($query);
        $this->itemTags = $db->loadObjectList();

        return $this->itemTags;
    }

    /**
     * Method to get a list of tags for multiple items, optionally with the
tag data.
     *
     * @param   string   $contentType  Content type alias. Dot separated.
     * @param   array    $ids          Id of the item to retrieve tags for.
     * @param   boolean  $getTagData   If true, data from the tags table
will be included, defaults to true.
     *
     * @return  array    Array of tag objects grouped by Id.
     *
     * @since   4.2.0
     */
    public function getMultipleItemTags($contentType, array $ids,
$getTagData = true)
    {
        $data = [];

        $ids = array_map('intval', $ids);

        /** @var DatabaseInterface $db */
        $db = Factory::getContainer()->get(DatabaseInterface::class);

        $query = $db->getQuery(true)
            ->select($db->quoteName(['m.tag_id',
'm.content_item_id']))
            ->from($db->quoteName('#__contentitem_tag_map',
'm'))
            ->where(
                [
                    $db->quoteName('m.type_alias') . ' =
:contentType',
                    $db->quoteName('t.published') . ' =
1',
                ]
            )
            ->whereIn($db->quoteName('m.content_item_id'),
$ids)
            ->bind(':contentType', $contentType);

        $query->join('INNER',
$db->quoteName('#__tags', 't'),
$db->quoteName('m.tag_id') . ' = ' .
$db->quoteName('t.id'));

        $groups = Factory::getUser()->getAuthorisedViewLevels();

        $query->whereIn($db->quoteName('t.access'),
$groups);

        // Optionally filter on language
        $language =
ComponentHelper::getParams('com_tags')->get('tag_list_language_filter',
'all');

        if ($language !== 'all') {
            if ($language === 'current_language') {
                $language = $this->getCurrentLanguage();
            }

            $query->whereIn($db->quoteName('language'),
[$language, '*'], ParameterType::STRING);
        }

        if ($getTagData) {
            $query->select($db->quoteName('t') .
'.*');
        }

        $db->setQuery($query);

        $rows = $db->loadObjectList();

        // Group data by item Id.
        foreach ($rows as $row) {
            $data[$row->content_item_id][] = $row;
            unset($row->content_item_id);
        }

        return $data;
    }

    /**
     * Method to get a list of tags for a given item.
     * Normally used for displaying a list of tags within a layout
     *
     * @param   mixed   $ids     The id or array of ids (primary key) of
the item to be tagged.
     * @param   string  $prefix  Dot separated string with the option and
view to be used for a url.
     *
     * @return  string   Comma separated list of tag Ids.
     *
     * @since   3.1
     */
    public function getTagIds($ids, $prefix)
    {
        if (empty($ids)) {
            return;
        }

        /**
         * Ids possible formats:
         * ---------------------
         *  $id = 1;
         *  $id = array(1,2);
         *  $id = array('1,3,4,19');
         *  $id = '1,3';
         */
        $ids = (array) $ids;
        $ids = implode(',', $ids);
        $ids = explode(',', $ids);
        $ids = ArrayHelper::toInteger($ids);

        $db = Factory::getDbo();

        // Load the tags.
        $query = $db->getQuery(true)
            ->select($db->quoteName('t.id'))
            ->from($db->quoteName('#__tags',
't'))
            ->join('INNER',
$db->quoteName('#__contentitem_tag_map', 'm'),
$db->quoteName('m.tag_id') . ' = ' .
$db->quoteName('t.id'))
            ->where($db->quoteName('m.type_alias') . '
= :prefix')
            ->whereIn($db->quoteName('m.content_item_id'),
$ids)
            ->bind(':prefix', $prefix);

        $db->setQuery($query);

        // Add the tags to the content data.
        $tagsList   = $db->loadColumn();
        $this->tags = implode(',', $tagsList);

        return $this->tags;
    }

    /**
     * Method to get a query to retrieve a detailed list of items for a
tag.
     *
     * @param   mixed    $tagId            Tag or array of tags to be
matched
     * @param   mixed    $typesr           Null, type or array of type
aliases for content types to be included in the results
     * @param   boolean  $includeChildren  True to include the results from
child tags
     * @param   string   $orderByOption    Column to order the results by
     * @param   string   $orderDir         Direction to sort the results in
     * @param   boolean  $anyOrAll         True to include items matching
at least one tag, false to include
     *                                     items all tags in the array.
     * @param   string   $languageFilter   Optional filter on language.
Options are 'all', 'current' or any string.
     * @param   string   $stateFilter      Optional filtering on
publication state, defaults to published or unpublished.
     *
     * @return  \Joomla\Database\DatabaseQuery  Query to retrieve a list of
tags
     *
     * @since   3.1
     */
    public function getTagItemsQuery(
        $tagId,
        $typesr = null,
        $includeChildren = false,
        $orderByOption = 'c.core_title',
        $orderDir = 'ASC',
        $anyOrAll = true,
        $languageFilter = 'all',
        $stateFilter = '0,1'
    ) {
        // Create a new query object.
        $db       = Factory::getDbo();
        $query    = $db->getQuery(true);
        $user     = Factory::getUser();
        $nullDate = $db->getNullDate();
        $nowDate  = Factory::getDate()->toSql();

        // Force ids to array and sanitize
        $tagIds = (array) $tagId;
        $tagIds = implode(',', $tagIds);
        $tagIds = explode(',', $tagIds);
        $tagIds = ArrayHelper::toInteger($tagIds);

        $ntagsr = \count($tagIds);

        // If we want to include children we have to adjust the list of
tags.
        // We do not search child tags when the match all option is
selected.
        if ($includeChildren) {
            $tagTreeArray = [];

            foreach ($tagIds as $tag) {
                $this->getTagTreeArray($tag, $tagTreeArray);
            }

            $tagIds = array_values(array_unique(array_merge($tagIds,
$tagTreeArray)));
        }

        // Sanitize filter states
        $stateFilters = explode(',', $stateFilter);
        $stateFilters = ArrayHelper::toInteger($stateFilters);

        // M is the mapping table. C is the core_content table. Ct is the
content_types table.
        $query->select(
            [
                $db->quoteName('m.type_alias'),
                $db->quoteName('m.content_item_id'),
                $db->quoteName('m.core_content_id'),
                'COUNT(' .
$db->quoteName('m.tag_id') . ') AS ' .
$db->quoteName('match_count'),
                'MAX(' .
$db->quoteName('m.tag_date') . ') AS ' .
$db->quoteName('tag_date'),
                'MAX(' .
$db->quoteName('c.core_title') . ') AS ' .
$db->quoteName('core_title'),
                'MAX(' .
$db->quoteName('c.core_params') . ') AS ' .
$db->quoteName('core_params'),
                'MAX(' .
$db->quoteName('c.core_alias') . ') AS ' .
$db->quoteName('core_alias'),
                'MAX(' .
$db->quoteName('c.core_body') . ') AS ' .
$db->quoteName('core_body'),
                'MAX(' .
$db->quoteName('c.core_state') . ') AS ' .
$db->quoteName('core_state'),
                'MAX(' .
$db->quoteName('c.core_access') . ') AS ' .
$db->quoteName('core_access'),
                'MAX(' .
$db->quoteName('c.core_metadata') . ') AS ' .
$db->quoteName('core_metadata'),
                'MAX(' .
$db->quoteName('c.core_created_user_id') . ') AS ' .
$db->quoteName('core_created_user_id'),
                'MAX(' .
$db->quoteName('c.core_created_by_alias') . ') AS' .
$db->quoteName('core_created_by_alias'),
                'MAX(' .
$db->quoteName('c.core_created_time') . ') AS ' .
$db->quoteName('core_created_time'),
                'MAX(' .
$db->quoteName('c.core_images') . ') AS ' .
$db->quoteName('core_images'),
                'CASE WHEN ' .
$db->quoteName('c.core_modified_time') . ' = :nullDate
THEN ' . $db->quoteName('c.core_created_time')
                . ' ELSE ' .
$db->quoteName('c.core_modified_time') . ' END AS '
. $db->quoteName('core_modified_time'),
                'MAX(' .
$db->quoteName('c.core_language') . ') AS ' .
$db->quoteName('core_language'),
                'MAX(' .
$db->quoteName('c.core_catid') . ') AS ' .
$db->quoteName('core_catid'),
                'MAX(' .
$db->quoteName('c.core_publish_up') . ') AS ' .
$db->quoteName('core_publish_up'),
                'MAX(' .
$db->quoteName('c.core_publish_down') . ') AS ' .
$db->quoteName('core_publish_down'),
                'MAX(' .
$db->quoteName('ct.type_title') . ') AS ' .
$db->quoteName('content_type_title'),
                'MAX(' . $db->quoteName('ct.router')
. ') AS ' . $db->quoteName('router'),
                'CASE WHEN ' .
$db->quoteName('c.core_created_by_alias') . ' > '
. $db->quote(' ')
                . ' THEN ' .
$db->quoteName('c.core_created_by_alias') . ' ELSE '
. $db->quoteName('ua.name') . ' END AS ' .
$db->quoteName('author'),
                $db->quoteName('ua.email',
'author_email'),
            ]
        )
            ->bind(':nullDate', $nullDate)
            ->from($db->quoteName('#__contentitem_tag_map',
'm'))
            ->join(
                'INNER',
                $db->quoteName('#__ucm_content',
'c'),
                $db->quoteName('m.type_alias') . ' =
' . $db->quoteName('c.core_type_alias')
                . ' AND ' .
$db->quoteName('m.core_content_id') . ' = ' .
$db->quoteName('c.core_content_id')
            )
            ->join('INNER',
$db->quoteName('#__content_types', 'ct'),
$db->quoteName('ct.type_alias') . ' = ' .
$db->quoteName('m.type_alias'));

        // Join over categories to get only tags from published categories
        $query->join('LEFT',
$db->quoteName('#__categories', 'tc'),
$db->quoteName('tc.id') . ' = ' .
$db->quoteName('c.core_catid'));

        // Join over the users for the author and email
        $query->join('LEFT',
$db->quoteName('#__users', 'ua'),
$db->quoteName('ua.id') . ' = ' .
$db->quoteName('c.core_created_user_id'))
            ->whereIn($db->quoteName('c.core_state'),
$stateFilters)
            ->whereIn($db->quoteName('m.tag_id'), $tagIds)
            ->extendWhere(
                'AND',
                [
                    $db->quoteName('c.core_catid') . ' =
0',
                    $db->quoteName('tc.published') . ' =
1',
                ],
                'OR'
            );

        // Get the type data, limited to types in the request if there are
any specified.
        $typesarray  = self::getTypes('assocList', $typesr,
false);
        $typeAliases = array_column($typesarray, 'type_alias');
        $query->whereIn($db->quoteName('m.type_alias'),
$typeAliases, ParameterType::STRING);

        $groups   =
array_values(array_unique($user->getAuthorisedViewLevels()));
        $groups[] = 0;
        $query->whereIn($db->quoteName('c.core_access'),
$groups);

        if (!\in_array(0, $stateFilters, true)) {
            $query->extendWhere(
                'AND',
                [
                    $db->quoteName('c.core_publish_up') .
' = :nullDate1',
                    $db->quoteName('c.core_publish_up') .
' IS NULL',
                    $db->quoteName('c.core_publish_up') .
' <= :nowDate1',
                ],
                'OR'
            )
                ->extendWhere(
                    'AND',
                    [
                        $db->quoteName('c.core_publish_down')
. ' = :nullDate2',
                        $db->quoteName('c.core_publish_down')
. ' IS NULL',
                        $db->quoteName('c.core_publish_down')
. ' >= :nowDate2',
                    ],
                    'OR'
                )
                ->bind([':nullDate1', ':nullDate2'],
$nullDate)
                ->bind([':nowDate1', ':nowDate2'],
$nowDate);
        }

        // Optionally filter on language
        if ($languageFilter !== 'all') {
            if ($languageFilter === 'current_language') {
                $languageFilter = $this->getCurrentLanguage();
            }

           
$query->whereIn($db->quoteName('c.core_language'),
[$languageFilter, '*'], ParameterType::STRING);
        }

        $query->group(
            [
                $db->quoteName('m.type_alias'),
                $db->quoteName('m.content_item_id'),
                $db->quoteName('m.core_content_id'),
                $db->quoteName('core_modified_time'),
                $db->quoteName('core_created_time'),
                $db->quoteName('core_created_by_alias'),
                $db->quoteName('author'),
                $db->quoteName('author_email'),
            ]
        );

        // Use HAVING if matching all tags and we are matching more than
one tag.
        if ($ntagsr > 1 && $anyOrAll != 1 &&
$includeChildren != 1) {
            // The number of results should equal the number of tags
requested.
            $query->having('COUNT(' .
$db->quoteName('m.tag_id') . ') = :ntagsr')
                ->bind(':ntagsr', $ntagsr,
ParameterType::INTEGER);
        }

        // Set up the order by using the option chosen
        if ($orderByOption === 'match_count') {
            $orderBy = 'COUNT(' .
$db->quoteName('m.tag_id') . ')';
        } else {
            $orderBy = 'MAX(' . $db->quoteName($orderByOption)
. ')';
        }

        $query->order($orderBy . ' ' . $orderDir);

        return $query;
    }

    /**
     * Function that converts tag ids to their tag names
     *
     * @param   array  $tagIds  Array of integer tag ids.
     *
     * @return  array  An array of tag names.
     *
     * @since   3.1
     */
    public function getTagNames($tagIds)
    {
        $tagNames = [];

        if (\is_array($tagIds) && \count($tagIds) > 0) {
            $tagIds = ArrayHelper::toInteger($tagIds);

            $db    = Factory::getDbo();
            $query = $db->getQuery(true)
                ->select($db->quoteName('title'))
                ->from($db->quoteName('#__tags'))
                ->whereIn($db->quoteName('id'), $tagIds)
                ->order($db->quoteName('title'));

            $db->setQuery($query);
            $tagNames = $db->loadColumn();
        }

        return $tagNames;
    }

    /**
     * Method to get an array of tag ids for the current tag and its
children
     *
     * @param   integer  $id             An optional ID
     * @param   array    &$tagTreeArray  Array containing the tag tree
     *
     * @return  mixed
     *
     * @since   3.1
     */
    public function getTagTreeArray($id, &$tagTreeArray = [])
    {
        // Get a level row instance.
        $table =
Factory::getApplication()->bootComponent('com_tags')->getMVCFactory()->createTable('Tag',
'Administrator');

        if ($table->isLeaf($id)) {
            $tagTreeArray[] = $id;

            return $tagTreeArray;
        }

        $tagTree = $table->getTree($id);

        // Attempt to load the tree
        if ($tagTree) {
            foreach ($tagTree as $tag) {
                $tagTreeArray[] = $tag->id;
            }

            return $tagTreeArray;
        }
    }

    /**
     * Method to get a list of types with associated data.
     *
     * @param   string   $arrayType    Optionally specify that the returned
list consist of objects, associative arrays, or arrays.
     *                                 Options are: rowList, assocList, and
objectList
     * @param   array    $selectTypes  Optional array of type ids or
aliases to limit the results to. Often from a request.
     * @param   boolean  $useAlias     If true, the alias is used to match,
if false the type_id is used.
     *
     * @return  array   Array of types
     *
     * @since   3.1
     */
    public static function getTypes($arrayType = 'objectList',
$selectTypes = null, $useAlias = true)
    {
        // Initialize some variables.
        $db    = Factory::getDbo();
        $query = $db->getQuery(true)
            ->select('*');

        if (!empty($selectTypes)) {
            $selectTypes = (array) $selectTypes;

            if ($useAlias) {
               
$query->whereIn($db->quoteName('type_alias'), $selectTypes,
ParameterType::STRING);
            } else {
                $selectTypes = ArrayHelper::toInteger($selectTypes);

                $query->whereIn($db->quoteName('type_id'),
$selectTypes);
            }
        }

        $query->from($db->quoteName('#__content_types'));

        $db->setQuery($query);

        switch ($arrayType) {
            case 'assocList':
                $types = $db->loadAssocList();
                break;

            case 'rowList':
                $types = $db->loadRowList();
                break;

            case 'objectList':
            default:
                $types = $db->loadObjectList();
                break;
        }

        return $types;
    }

    /**
     * Function that handles saving tags used in a table class after a
store()
     *
     * @param   TableInterface  $table    Table being processed
     * @param   array           $newTags  Array of new tags
     * @param   boolean         $replace  Flag indicating if all existing
tags should be replaced
     *
     * @return  boolean
     *
     * @since   3.1
     */
    public function postStoreProcess(TableInterface $table, $newTags = [],
$replace = true)
    {
        if (!empty($table->newTags) && empty($newTags)) {
            $newTags = $table->newTags;
        }

        // If existing row, check to see if tags have changed.
        $newTable = clone $table;
        $newTable->reset();

        $result = true;

        // Process ucm_content and ucm_base if either tags have changed or
we have some tags.
        if ($this->tagsChanged || (!empty($newTags) &&
$newTags[0] != '')) {
            if (!$newTags && $replace == true) {
                // Delete all tags data
                $key    = $table->getKeyName();
                $result = $this->deleteTagData($table, $table->$key);
            } else {
                // Process the tags
                $data            = $this->getRowData($table);
                $ucmContentTable =
Table::getInstance('CoreContent');

                $ucm     = new UCMContent($table, $this->typeAlias);
                $ucmData = $data ? $ucm->mapData($data) :
$ucm->ucmData;

                $primaryId =
$ucm->getPrimaryKey($ucmData['common']['core_type_id'],
$ucmData['common']['core_content_item_id']);
                $result    = $ucmContentTable->load($primaryId);
                $result    = $result &&
$ucmContentTable->bind($ucmData['common']);
                $result    = $result &&
$ucmContentTable->check();
                $result    = $result &&
$ucmContentTable->store();
                $ucmId     = $ucmContentTable->core_content_id;

                // Store the tag data if the article data was saved and run
related methods.
                $result = $result && $this->tagItem($ucmId,
$table, $newTags, $replace);
            }
        }

        return $result;
    }

    /**
     * Function that preProcesses data from a table prior to a store() to
ensure proper tag handling
     *
     * @param   TableInterface  $table    Table being processed
     * @param   array           $newTags  Array of new tags
     *
     * @return  null
     *
     * @since   3.1
     */
    public function preStoreProcess(TableInterface $table, $newTags = [])
    {
        if ($newTags != []) {
            $this->newTags = $newTags;
        }

        // If existing row, check to see if tags have changed.
        $oldTable = clone $table;
        $oldTable->reset();
        $key       = $oldTable->getKeyName();
        $typeAlias = $this->typeAlias;

        if ($oldTable->$key && $oldTable->load()) {
            $this->oldTags = $this->getTagIds($oldTable->$key,
$typeAlias);
        }

        // New items with no tags bypass this step.
        if ((!empty($newTags) && \is_string($newTags) ||
(isset($newTags[0]) && $newTags[0] != '')) ||
isset($this->oldTags)) {
            if (\is_array($newTags)) {
                $newTags = implode(',', $newTags);
            }

            // We need to process tags if the tags have changed or if we
have a new row
            $this->tagsChanged = (empty($this->oldTags) &&
!empty($newTags)) || (!empty($this->oldTags) &&
$this->oldTags != $newTags) || !$table->$key;
        }
    }

    /**
     * Function to search tags
     *
     * @param   array  $filters  Filter to apply to the search
     *
     * @return  array
     *
     * @since   3.1
     */
    public static function searchTags($filters = [])
    {
        $db    = Factory::getDbo();
        $query = $db->getQuery(true)
            ->select(
                [
                    $db->quoteName('a.id', 'value'),
                    $db->quoteName('a.path',
'text'),
                    $db->quoteName('a.path'),
                ]
            )
            ->from($db->quoteName('#__tags',
'a'))
            ->join(
                'LEFT',
                $db->quoteName('#__tags', 'b'),
                $db->quoteName('a.lft') . ' > ' .
$db->quoteName('b.lft') . ' AND ' .
$db->quoteName('a.rgt') . ' < ' .
$db->quoteName('b.rgt')
            );

        // Do not return root
        $query->where($db->quoteName('a.alias') . '
<> ' . $db->quote('root'));

        // Filter language
        if (!empty($filters['flanguage'])) {
            $query->whereIn($db->quoteName('a.language'),
[$filters['flanguage'], '*'], ParameterType::STRING);
        }

        // Search in title or path
        if (!empty($filters['like'])) {
            $search = '%' . $filters['like'] .
'%';
            $query->extendWhere(
                'AND',
                [
                    $db->quoteName('a.title') . ' LIKE
:search1',
                    $db->quoteName('a.path') . ' LIKE
:search2',
                ],
                'OR'
            )
                ->bind([':search1', ':search2'],
$search);
        }

        // Filter title
        if (!empty($filters['title'])) {
            $query->where($db->quoteName('a.title') .
' = :title')
                ->bind(':title', $filters['title']);
        }

        // Filter on the published state
        if (isset($filters['published']) &&
is_numeric($filters['published'])) {
            $published = (int) $filters['published'];
            $query->where($db->quoteName('a.published') .
' = :published')
                ->bind(':published', $published,
ParameterType::INTEGER);
        }

        // Filter on the access level
        if (isset($filters['access']) &&
\is_array($filters['access']) &&
\count($filters['access'])) {
            $groups = ArrayHelper::toInteger($filters['access']);
            $query->whereIn($db->quoteName('a.access'),
$groups);
        }

        // Filter by parent_id
        if (isset($filters['parent_id']) &&
is_numeric($filters['parent_id'])) {
            $tagTable =
Factory::getApplication()->bootComponent('com_tags')->getMVCFactory()->createTable('Tag',
'Administrator');

            if ($children =
$tagTable->getTree($filters['parent_id'])) {
                $childrenIds = array_column($children, 'id');

                $query->whereIn($db->quoteName('a.id'),
$childrenIds);
            }
        }

        $query->group(
            [
                $db->quoteName('a.id'),
                $db->quoteName('a.title'),
                $db->quoteName('a.level'),
                $db->quoteName('a.lft'),
                $db->quoteName('a.rgt'),
                $db->quoteName('a.parent_id'),
                $db->quoteName('a.published'),
                $db->quoteName('a.path'),
            ]
        )
            ->order($db->quoteName('a.lft') . '
ASC');

        // Get the options.
        $db->setQuery($query);

        try {
            $results = $db->loadObjectList();
        } catch (\RuntimeException $e) {
            return [];
        }

        // We will replace path aliases with tag names
        return self::convertPathsToNames($results);
    }

    /**
     * Method to delete all instances of a tag from the mapping table.
Generally used when a tag is deleted.
     *
     * @param   integer  $tagId  The tag_id (primary key) for the deleted
tag.
     *
     * @return  void
     *
     * @since   3.1
     */
    public function tagDeleteInstances($tagId)
    {
        // Cast as integer until method is typehinted.
        $tag_id = (int) $tagId;

        // Delete the old tag maps.
        $db    = Factory::getDbo();
        $query = $db->getQuery(true)
           
->delete($db->quoteName('#__contentitem_tag_map'))
            ->where($db->quoteName('tag_id') . ' =
:id')
            ->bind(':id', $tagId, ParameterType::INTEGER);
        $db->setQuery($query);
        $db->execute();
    }

    /**
     * Method to add or update tags associated with an item.
     *
     * @param   integer         $ucmId    Id of the #__ucm_content item
being tagged
     * @param   TableInterface  $table    Table object being tagged
     * @param   array           $tags     Array of tags to be applied.
     * @param   boolean         $replace  Flag indicating if all existing
tags should be replaced
     *
     * @return  boolean  true on success, otherwise false.
     *
     * @since   3.1
     */
    public function tagItem($ucmId, TableInterface $table, $tags = [],
$replace = true)
    {
        $key     = $table->getKeyName();
        $oldTags = $this->getTagIds((int) $table->$key,
$this->typeAlias);
        $oldTags = explode(',', $oldTags);
        $result  = $this->unTagItem($ucmId, $table);

        if ($replace) {
            $newTags = $tags;
        } else {
            if ($tags == []) {
                $newTags = $table->newTags;
            } else {
                $newTags = $tags;
            }

            if ($oldTags[0] != '') {
                $newTags = array_unique(array_merge($newTags, $oldTags));
            }
        }

        if (\is_array($newTags) && \count($newTags) > 0
&& $newTags[0] != '') {
            $result = $result && $this->addTagMapping($ucmId,
$table, $newTags);
        }

        return $result;
    }

    /**
     * Method to untag an item
     *
     * @param   integer         $contentId  ID of the content item being
untagged
     * @param   TableInterface  $table      Table object being untagged
     * @param   array           $tags       Array of tags to be untagged.
Use an empty array to untag all existing tags.
     *
     * @return  boolean  true on success, otherwise false.
     *
     * @since   3.1
     */
    public function unTagItem($contentId, TableInterface $table, $tags =
[])
    {
        $key   = $table->getKeyName();
        $id    = (int) $table->$key;
        $db    = Factory::getDbo();
        $query = $db->getQuery(true)
           
->delete($db->quoteName('#__contentitem_tag_map'))
            ->where(
                [
                    $db->quoteName('type_alias') . ' =
:type',
                    $db->quoteName('content_item_id') . '
= :id',
                ]
            )
            ->bind(':type', $this->typeAlias)
            ->bind(':id', $id, ParameterType::INTEGER);

        if (\is_array($tags) && \count($tags) > 0) {
            $tags = ArrayHelper::toInteger($tags);

            $query->whereIn($db->quoteName('tag_id'),
$tags);
        }

        $db->setQuery($query);

        return (bool) $db->execute();
    }

    /**
     * Function that converts tag ids to their tag id and tag names
     *
     * @param   array  $tagIds  Array of integer tag ids.
     *
     * @return  array  An array of tag id and name.
     *
     * @since   4.4.0
     */
    public function getTags($tagIds)
    {
        $tagNames = [];

        if (\is_array($tagIds) && \count($tagIds) > 0) {
            $tagIds = ArrayHelper::toInteger($tagIds);

            $db    = Factory::getDbo();
            $query = $db->getQuery(true)
                ->select([$db->quoteName('id'),
$db->quoteName('title')])
                ->from($db->quoteName('#__tags'))
                ->whereIn($db->quoteName('id'), $tagIds)
                ->order($db->quoteName('title'));

            $db->setQuery($query);
            $tagNames = $db->loadAssocList('id',
'title');
        }

        return $tagNames;
    }
}
PKlE�[��99PublicFolderGeneratorHelper.phpnu�[���<?php

/**
 * Joomla! Content Management System
 *
 * @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\CMS\Helper;

use Joomla\CMS\Plugin\PluginHelper;
use Joomla\Registry\Registry;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Admin Component Public Folder Generator Helper
 *
 * @since  5.0.0
 */
class PublicFolderGeneratorHelper
{
    /**
     * Files and folders to be symlinked
     *
     * @var array
     */
    private $filesSymLink = [
        // Administrator
        'administrator/components/com_joomlaupdate/extract.php',

        // Media static assets
        'media',
    ];

    /**
     * The template for the defines.php file
     *
     * @var string
     */
    private $definesTemplate = <<<PHP
<?php

/**
 * Programmatically generated
 *
 * @copyright  (C) 2005 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license    GNU General Public License version 2 or later; see
LICENSE.txt
 */

define('JPATH_ROOT', {{ROOTFOLDER}});
define('JPATH_PUBLIC', {{PUBLICFOLDER}});
define('JPATH_BASE', JPATH_ROOT . \$applicationPath);

PHP;

    /**
     * The template for the index.php file
     *
     * @var string
     */
    private $indexTemplate = <<<PHP
<?php

/**
 * Programmatically generated
 *
 * @copyright  (C) 2005 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license    GNU General Public License version 2 or later; see
LICENSE.txt
 */

\$applicationPath = {{APPLICATIONPATH}};

require_once {{DEFINESPATH}} . '/defines.php';

unset(\$applicationPath);

require_once JPATH_BASE . '/index.php';

PHP;

    /**
     * Creates a public folder
     *
     * @param string $destinationPath The full path for the public folder
     *
     * @return void
     * @throws \Exception
     *
     * @since  5.0.0
     */
    public function createPublicFolder(string $destinationPath): void
    {
        $destinationPath     = rtrim($destinationPath, '/\\') .
'/';
        $fullDestinationPath = $destinationPath;
        $definePublic        = '\'' . $destinationPath .
'\'';
        $root                = JPATH_ROOT . '/';
        $defineRoot          = '\'' . JPATH_ROOT .
'\'';

        if (substr($destinationPath, 0, 1) !== '/') {
            $fullDestinationPath = JPATH_ROOT . '/' .
$destinationPath;
            $root                = '';
            $dirsToRoot          = substr_count($destinationPath,
'/');
            $defineRoot          = str_repeat('dirname(',
$dirsToRoot) . '__DIR__' . str_repeat(')',
$dirsToRoot);
            $definePublic        = 'JPATH_ROOT . \'/' .
rtrim($destinationPath, '/') . '\'';
        }

        if (file_exists($fullDestinationPath . '/index.php')) {
            throw new \Exception('Unable to create the given folder,
index.php already exists.');
        }

        if ((!is_dir($fullDestinationPath) &&
!mkdir($fullDestinationPath, 0755, true))) {
            throw new \Exception('Unable to create the given folder,
check the permissions.');
        }

        // Create the required folders
        if (
            !mkdir($fullDestinationPath .
'/administrator/components/com_joomlaupdate', 0755, true)
            || !mkdir($fullDestinationPath . '/api', 0755, true)
        ) {
            throw new \Exception('Unable to create the given folder,
check the permissions.');
        }

        // Create essential symlinks
        foreach ($this->filesSymLink as $localDirectory) {
            $this->createSymlink($root . $localDirectory,
$destinationPath . $localDirectory, JPATH_ROOT . '/');
        }

        $filesHardCopies = [];

        // Copy the robots
        if (is_file(JPATH_ROOT . '/robots.txt')) {
            $filesHardCopies[] = '/robots.txt';
        } elseif (is_file(JPATH_ROOT . '/robots.txt.dist')) {
            $filesHardCopies[] = '/robots.txt.dist';
        }

        // Copy the apache config
        if (is_file(JPATH_ROOT . '/.htaccess')) {
            $filesHardCopies[] = '/.htaccess';
        } elseif (is_file(JPATH_ROOT . '/htaccess.txt')) {
            $filesHardCopies[] = '/htaccess.txt';
        }

        foreach ($filesHardCopies as $file) {
            $this->createFile($fullDestinationPath . $file,
file_get_contents(JPATH_ROOT . $file));
        }

        // Create the defines.php
        $this->createFile($fullDestinationPath .
'defines.php', str_replace(['{{ROOTFOLDER}}',
'{{PUBLICFOLDER}}'], [$defineRoot, $definePublic],
$this->definesTemplate));

        // The root index.php
        $this->createFile($fullDestinationPath . 'index.php',
str_replace(['{{APPLICATIONPATH}}', '{{DEFINESPATH}}'],
['\'\'', '__DIR__'],
$this->indexTemplate));

        // The Administrator root index.php
        $this->createFile($fullDestinationPath .
'administrator/index.php',
str_replace(['{{APPLICATIONPATH}}', '{{DEFINESPATH}}'],
['\'' . DIRECTORY_SEPARATOR .
'administrator\'', 'dirname(__DIR__)'],
$this->indexTemplate));

        // The API root index.php
        $this->createFile($fullDestinationPath .
'api/index.php', str_replace(['{{APPLICATIONPATH}}',
'{{DEFINESPATH}}'], ['\'' . DIRECTORY_SEPARATOR .
'api\'', 'dirname(__DIR__)'],
$this->indexTemplate));

        // Get all the local filesystem directories
        if (\defined('_JCLI_INSTALLATION')) {
            $localDirectories = [(object)['directory' =>
'images']];
        } elseif (PluginHelper::isEnabled('filesystem',
'local')) {
            $local            =
PluginHelper::getPlugin('filesystem', 'local');
            $localDirectories = (new
Registry($local->params))->get('directories',
[(object)['directory' => 'images']]);
        }

        // Symlink all the local filesystem directories
        foreach ($localDirectories as $localDirectory) {
            if (!is_link($destinationPath . '/' .
$localDirectory->directory)) {
                $this->createSymlink($root .
$localDirectory->directory, $destinationPath .
$localDirectory->directory, JPATH_ROOT . '/');
            }
        }
    }

    /**
     * Creates a symlink
     *
     * @param string $source The source path
     * @param string $dest The destination path
     * @param string $base The base path if relative
     *
     * @return void
     *
     * @since  5.0.0
     */
    private function createSymlink(string $source, string $dest, string
$base): void
    {
        if (substr($source, 0, 1) !== '/') {
            $source = str_repeat('../', substr_count($dest,
'/')) . $source;
            $dest   = $base . $dest;
        }

        if (!symlink($source, $dest)) {
            throw new \Exception('Unable to symlink the file: ' .
str_replace(JPATH_ROOT, '', $source));
        }
    }

    /**
     * Writes the content to a given file
     *
     * @param string $path The destination path
     * @param string $content The contents of the file
     *
     * @return void
     *
     * @since  5.0.0
     */
    private function createFile(string $path, string $content): void
    {
        if (!file_put_contents($path, $content)) {
            throw new \Exception('Unable to create the file: ' .
$path);
        }
    }
}
PKlE�[�
�f��HelperFactoryInterface.phpnu�[���<?php

/**
 * Joomla! Content Management System
 *
 * @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\CMS\Helper;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Factory to load helper classes.
 *
 * @since  4.0.0
 */
interface HelperFactoryInterface
{
    /**
     * Returns a helper instance for the given name.
     *
     * @param   string  $name    The name
     * @param   array   $config  The config
     *
     * @return  \stdClass
     *
     * @since   4.0.0
     */
    public function getHelper(string $name, array $config = []);
}
PKlE�[žȀTTHelperFactory.phpnu�[���<?php

/**
 * Joomla! Content Management System
 *
 * @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\CMS\Helper;

use Joomla\Database\DatabaseAwareInterface;
use Joomla\Database\DatabaseAwareTrait;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Namespace based implementation of the HelperFactoryInterface
 *
 * @since  4.0.0
 */
class HelperFactory implements HelperFactoryInterface
{
    use DatabaseAwareTrait;

    /**
     * The extension namespace
     *
     * @var  string
     *
     * @since   4.0.0
     */
    private $namespace;

    /**
     * HelperFactory constructor.
     *
     * @param   string  $namespace  The namespace
     *
     * @since   4.0.0
     */
    public function __construct(string $namespace)
    {
        $this->namespace = $namespace;
    }

    /**
     * Returns a helper instance for the given name.
     *
     * @param   string  $name    The name
     * @param   array   $config  The config
     *
     * @return  \stdClass
     *
     * @since   4.0.0
     */
    public function getHelper(string $name, array $config = [])
    {
        $className = '\\' . trim($this->namespace,
'\\') . '\\' . $name;

        if (!class_exists($className)) {
            return null;
        }

        $helper = new $className($config);

        if ($helper instanceof DatabaseAwareInterface) {
            $helper->setDatabase($this->getDatabase());
        }

        return $helper;
    }
}
PKlE�[����!�!RouteHelper.phpnu�[���<?php

/**
 * Joomla! Content Management System
 *
 * @copyright  (C) 2013 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license    GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\CMS\Helper;

use Joomla\CMS\Categories\Categories;
use Joomla\CMS\Categories\CategoryNode;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Multilanguage;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Route Helper
 *
 * A class providing basic routing for urls that are for content types
found in
 * the #__content_types table and rows found in the #__ucm_content table.
 *
 * @since  3.1
 */
class RouteHelper
{
    /**
     * @var    array  Holds the reverse lookup
     * @since  3.1
     */
    protected static $lookup;

    /**
     * @var    string  Option for the extension (such as com_content)
     * @since  3.1
     */
    protected $extension;

    /**
     * @var    string  Value of the primary key in the content type table
     * @since  3.1
     */
    protected $id;

    /**
     * @var    string  Name of the view for the url
     * @since  3.1
     */
    protected $view;

    /**
     * A method to get the route for a specific item
     *
     * @param   integer  $id         Value of the primary key for the item
in its content table
     * @param   string   $typealias  The type_alias for the item being
routed. Of the form extension.view.
     * @param   string   $link       The link to be routed
     * @param   string   $language   The language of the content for
multilingual sites
     * @param   integer  $catid      Optional category id
     *
     * @return  string  The route of the item
     *
     * @since   3.1
     */
    public function getRoute($id, $typealias, $link = '',
$language = null, $catid = null)
    {
        $typeExploded = explode('.', $typealias);

        if (isset($typeExploded[1])) {
            $this->view      = $typeExploded[1];
            $this->extension = $typeExploded[0];
        } else {
            $this->view      =
Factory::getApplication()->getInput()->getString('view');
            $this->extension =
Factory::getApplication()->getInput()->getCmd('option');
        }

        $name = ucfirst(substr_replace($this->extension, '',
0, 4));

        $needles = [];

        if (isset($this->view)) {
            $needles[$this->view] = [(int) $id];
        }

        if (empty($link)) {
            // Create the link
            $link = 'index.php?option=' . $this->extension .
'&view=' . $this->view . '&id=' . $id;
        }

        if ($catid > 1) {
            $categories = Categories::getInstance($name);

            if ($categories) {
                $category = $categories->get((int) $catid);

                if ($category) {
                    $needles['category']   =
array_reverse($category->getPath());
                    $needles['categories'] =
$needles['category'];
                    $link .= '&catid=' . $catid;
                }
            }
        }

        // Deal with languages only if needed
        if (!empty($language) && $language !== '*'
&& Multilanguage::isEnabled()) {
            $link .= '&lang=' . $language;
            $needles['language'] = $language;
        }

        if ($item = $this->findItem($needles)) {
            $link .= '&Itemid=' . $item;
        }

        return $link;
    }

    /**
     * Method to find the item in the menu structure
     *
     * @param   array  $needles  Array of lookup values
     *
     * @return  mixed
     *
     * @since   3.1
     */
    protected function findItem($needles = [])
    {
        $app      = Factory::getApplication();
        $menus    = $app->getMenu('site');
        $language = $needles['language'] ?? '*';

        // $this->extension may not be set if coming from a static
method, check it
        if ($this->extension === null) {
            $this->extension =
$app->getInput()->getCmd('option');
        }

        // Prepare the reverse lookup array.
        if (!isset(static::$lookup[$language])) {
            static::$lookup[$language] = [];

            $component =
ComponentHelper::getComponent($this->extension);

            $attributes = ['component_id'];
            $values     = [$component->id];

            if ($language !== '*') {
                $attributes[] = 'language';
                $values[]     = [$needles['language'],
'*'];
            }

            $items = $menus->getItems($attributes, $values);

            foreach ($items as $item) {
                if (isset($item->query['view'])) {
                    $view = $item->query['view'];

                    if (!isset(static::$lookup[$language][$view])) {
                        static::$lookup[$language][$view] = [];
                    }

                    if (isset($item->query['id'])) {
                        if (\is_array($item->query['id'])) {
                            $item->query['id'] =
$item->query['id'][0];
                        }

                        /*
                         * Here it will become a bit tricky
                         * $language != * can override existing entries
                         * $language == * cannot override existing entries
                         */
                        if ($item->language !== '*' ||
!isset(static::$lookup[$language][$view][$item->query['id']]))
{
                           
static::$lookup[$language][$view][$item->query['id']] =
$item->id;
                        }
                    }
                }
            }
        }

        if ($needles) {
            foreach ($needles as $view => $ids) {
                if (isset(static::$lookup[$language][$view])) {
                    foreach ($ids as $id) {
                        if (isset(static::$lookup[$language][$view][(int)
$id])) {
                            return static::$lookup[$language][$view][(int)
$id];
                        }
                    }
                }
            }
        }

        $active = $menus->getActive();

        if ($active && $active->component ===
$this->extension && ($active->language === '*' ||
!Multilanguage::isEnabled())) {
            return $active->id;
        }

        // If not found, return language specific home link
        $default = $menus->getDefault($language);

        return !empty($default->id) ? $default->id : null;
    }

    /**
     * Fetches the category route
     *
     * @param   mixed   $catid      Category ID or CategoryNode instance
     * @param   mixed   $language   Language code
     * @param   string  $extension  Extension to lookup
     *
     * @return  string
     *
     * @since   3.2
     *
     * @throws  \InvalidArgumentException
     */
    public static function getCategoryRoute($catid, $language = 0,
$extension = '')
    {
        // Note: $extension is required but has to be an optional argument
in the function call due to argument order
        if (empty($extension)) {
            throw new \InvalidArgumentException(sprintf('$extension is
a required argument in %s()', __METHOD__));
        }

        if ($catid instanceof CategoryNode) {
            $id       = $catid->id;
            $category = $catid;
        } else {
            $extensionName = ucfirst(substr($extension, 4));
            $id            = (int) $catid;
            $category      =
Categories::getInstance($extensionName)->get($id);
        }

        if ($id < 1) {
            $link = '';
        } else {
            $link = 'index.php?option=' . $extension .
'&view=category&id=' . $id;

            $needles = [
                'category' => [$id],
            ];

            if ($language && $language !== '*' &&
Multilanguage::isEnabled()) {
                $link .= '&lang=' . $language;
                $needles['language'] = $language;
            }

            // Create the link
            if ($category) {
                $catids                =
array_reverse($category->getPath());
                $needles['category']   = $catids;
                $needles['categories'] = $catids;
            }

            if ($item = static::lookupItem($needles)) {
                $link .= '&Itemid=' . $item;
            }
        }

        return $link;
    }

    /**
     * Static alias to findItem() used to find the item in the menu
structure
     *
     * @param   array  $needles  Array of lookup values
     *
     * @return  mixed
     *
     * @since   3.2
     */
    protected static function lookupItem($needles = [])
    {
        $instance = new static();

        return $instance->findItem($needles);
    }
}
PKlE�[�Ư��`�`ModuleHelper.phpnu�[���<?php

/**
 * Joomla! Content Management System
 *
 * @copyright  (C) 2005 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license    GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\CMS\Helper;

use Joomla\CMS\Cache\CacheControllerFactoryInterface;
use Joomla\CMS\Cache\Controller\CallbackController;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Event\Module;
use Joomla\CMS\Factory;
use Joomla\CMS\Filter\InputFilter;
use Joomla\CMS\Language\LanguageHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\Log\Log;
use Joomla\CMS\Profiler\Profiler;
use Joomla\Database\ParameterType;
use Joomla\Filesystem\Path;
use Joomla\Registry\Registry;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Module helper class
 *
 * @since  1.5
 */
abstract class ModuleHelper
{
    /**
     * Get module by name (real, eg 'Breadcrumbs' or folder, eg
'mod_breadcrumbs')
     *
     * @param   string  $name   The name of the module
     * @param   string  $title  The title of the module, optional
     *
     * @return  \stdClass  The Module object
     *
     * @since   1.5
     */
    public static function &getModule($name, $title = null)
    {
        $result  = null;
        $modules =& static::load();
        $total   = \count($modules);

        for ($i = 0; $i < $total; $i++) {
            // Match the name of the module
            if ($modules[$i]->name === $name || $modules[$i]->module
=== $name) {
                // Match the title if we're looking for a specific
instance of the module
                if (!$title || $modules[$i]->title === $title) {
                    // Found it
                    $result = &$modules[$i];
                    break;
                }
            }
        }

        // If we didn't find it, and the name is mod_something, create
a dummy object
        if ($result === null && strpos($name, 'mod_') ===
0) {
            $result         = static::createDummyModule();
            $result->module = $name;
        }

        return $result;
    }

    /**
     * Get modules by position
     *
     * @param   string  $position  The position of the module
     *
     * @return  array  An array of module objects
     *
     * @since   1.5
     */
    public static function &getModules($position)
    {
        $position = strtolower($position);
        $result   = [];
        $input    = Factory::getApplication()->getInput();
        $modules  = &static::load();
        $total    = \count($modules);

        for ($i = 0; $i < $total; $i++) {
            if ($modules[$i]->position === $position) {
                $result[] = &$modules[$i];
            }
        }

        // Prepend a dummy module for template preview if no module is
published in the position
        if (empty($result) && $input->getBool('tp')
&&
ComponentHelper::getParams('com_templates')->get('template_positions_display'))
{
            $dummy                  = static::createDummyModule();
            $dummy->title           = $position;
            $dummy->position        = $position;
            $dummy->content         = $position;
            $dummy->contentRendered = true;

            array_unshift($result, $dummy);
        }

        return $result;
    }

    /**
     * Checks if a module is enabled. A given module will only be returned
     * if it meets the following criteria: it is enabled, it is assigned to
     * the current menu item or all items, and the user meets the access
level
     * requirements.
     *
     * @param   string  $module  The module name
     *
     * @return  boolean See description for conditions.
     *
     * @since   1.5
     */
    public static function isEnabled($module)
    {
        $result = static::getModule($module);

        return $result !== null && $result->id !== 0;
    }

    /**
     * Render the module.
     *
     * @param   object  $module   A module object.
     * @param   array   $attribs  An array of attributes for the module
(probably from the XML).
     *
     * @return  string  The HTML content of the module output.
     *
     * @since   1.5
     */
    public static function renderModule($module, $attribs = [])
    {
        $app = Factory::getApplication();

        // Check that $module is a valid module object
        if (!\is_object($module) || !isset($module->module) ||
!isset($module->params)) {
            if (JDEBUG) {
                Log::addLogger(['text_file' =>
'jmodulehelper.log.php'], Log::ALL, ['modulehelper']);
                $app->getLogger()->debug(
                    __METHOD__ . '() - The $module parameter should be
a module object.',
                    ['category' => 'modulehelper']
                );
            }

            return '';
        }

        // Get module parameters
        $params = new Registry($module->params);

        // Render the module content
        static::renderRawModule($module, $params, $attribs);

        // Return early if only the content is required
        if (!empty($attribs['contentOnly'])) {
            return $module->content;
        }

        if (JDEBUG) {
           
Profiler::getInstance('Application')->mark('beforeRenderModule
' . $module->module . ' (' . $module->title .
')');
        }

        // Record the scope.
        $scope = $app->scope;

        // Set scope to component name
        $app->scope = $module->module;
        $dispatcher = $app->getDispatcher();

        // Get the template
        $template = $app->getTemplate();

        // Check if the current module has a style param to override
template module style
        $paramsChromeStyle = $params->get('style');
        $basePath          = '';

        if ($paramsChromeStyle) {
            $paramsChromeStyle   = explode('-',
$paramsChromeStyle, 2);
            $ChromeStyleTemplate = strtolower($paramsChromeStyle[0]);
            $attribs['style']    = $paramsChromeStyle[1];

            // Only set $basePath if the specified template isn't the
current or system one.
            if ($ChromeStyleTemplate !== $template &&
$ChromeStyleTemplate !== 'system') {
                $basePath = JPATH_THEMES . '/' .
$ChromeStyleTemplate . '/html/layouts';
            }
        }

        // Make sure a style is set
        if (!isset($attribs['style'])) {
            $attribs['style'] = 'none';
        }

        // Dynamically add outline style
        if ($app->getInput()->getBool('tp') &&
ComponentHelper::getParams('com_templates')->get('template_positions_display'))
{
            $attribs['style'] .= ' outline';
        }

        $module->style = $attribs['style'];

        // If the $module is nulled it will return an empty content,
otherwise it will render the module normally.
        $brEvent = $dispatcher->dispatch('onRenderModule', new
Module\BeforeRenderModuleEvent('onRenderModule', [
            'subject'    => $module,
            'attributes' => &$attribs, // @todo: Remove
reference in Joomla 6, see BeforeRenderModuleEvent::__constructor()
        ]));
        // Get final attributes
        $attribs = $brEvent->getArgument('attributes',
$attribs);

        if (!isset($module->content)) {
            return '';
        }

        // Prevent double modification of the module content by chrome
style
        $module = clone $module;

        $displayData = [
            'module'  => $module,
            'params'  => $params,
            'attribs' => $attribs,
        ];

        foreach (explode(' ', $attribs['style']) as
$style) {
            $moduleContent = LayoutHelper::render('chromes.' .
$style, $displayData, $basePath);

            if ($moduleContent) {
                $module->content = $moduleContent;
            }
        }

        // Revert the scope
        $app->scope = $scope;

        $dispatcher->dispatch('onAfterRenderModule', new
Module\AfterRenderModuleEvent('onAfterRenderModule', [
            'subject'    => $module,
            'attributes' => $attribs,
        ]));

        if (JDEBUG) {
           
Profiler::getInstance('Application')->mark('afterRenderModule
' . $module->module . ' (' . $module->title .
')');
        }

        return $module->content;
    }

    /**
     * Render the module content.
     *
     * @param   object    $module   A module object
     * @param   Registry  $params   A module parameters
     * @param   array     $attribs  An array of attributes for the module
(probably from the XML).
     *
     * @return  string
     *
     * @since   4.0.0
     */
    public static function renderRawModule($module, Registry $params,
$attribs = [])
    {
        if (!empty($module->contentRendered)) {
            return $module->content;
        }

        if (JDEBUG) {
           
Profiler::getInstance('Application')->mark('beforeRenderRawModule
' . $module->module . ' (' . $module->title .
')');
        }

        $app = Factory::getApplication();

        // Record the scope.
        $scope = $app->scope;

        // Set scope to component name
        $app->scope = $module->module;

        // Get module path
        $module->module = preg_replace('/[^A-Z0-9_\.-]/i',
'', $module->module);

        $dispatcher = $app->bootModule($module->module,
$app->getName())->getDispatcher($module, $app);

        // Check if we have a dispatcher
        if ($dispatcher) {
            ob_start();
            $dispatcher->dispatch();
            $module->content = ob_get_clean();
        }

        // Add the flag that the module content has been rendered
        $module->contentRendered = true;

        // Revert the scope
        $app->scope = $scope;

        if (JDEBUG) {
           
Profiler::getInstance('Application')->mark('afterRenderRawModule
' . $module->module . ' (' . $module->title .
')');
        }

        return $module->content;
    }

    /**
     * Get the path to a layout for a module
     *
     * @param   string  $module  The name of the module
     * @param   string  $layout  The name of the module layout. If
alternative layout, in the form template:filename.
     *
     * @return  string  The path to the module layout
     *
     * @since   1.5
     */
    public static function getLayoutPath($module, $layout =
'default')
    {
        $templateObj   = Factory::getApplication()->getTemplate(true);
        $defaultLayout = $layout;
        $template      = $templateObj->template;

        if (strpos($layout, ':') !== false) {
            // Get the template and file name from the string
            $temp          = explode(':', $layout);
            $template      = $temp[0] === '_' ? $template :
$temp[0];
            $layout        = $temp[1];
            $defaultLayout = $temp[1] ?: 'default';
        }

        $dPath = JPATH_BASE . '/modules/' . $module .
'/tmpl/default.php';

        try {
            // Build the template and base path for the layout
            $tPath = Path::check(JPATH_THEMES . '/' . $template .
'/html/' . $module . '/' . $layout . '.php');
            $iPath = Path::check(JPATH_THEMES . '/' .
$templateObj->parent . '/html/' . $module . '/' .
$layout . '.php');
            $bPath = Path::check(JPATH_BASE . '/modules/' .
$module . '/tmpl/' . $defaultLayout . '.php');
        } catch (\Exception $e) {
            // On error fallback to the default path
            return $dPath;
        }

        // If the template has a layout override use it
        if (is_file($tPath)) {
            return $tPath;
        }

        if (!empty($templateObj->parent) && is_file($iPath)) {
            return $iPath;
        }

        if (is_file($bPath)) {
            return $bPath;
        }

        return $dPath;
    }

    /**
     * Load published modules.
     *
     * @return  array
     *
     * @since   3.2
     */
    protected static function &load()
    {
        static $modules;

        if (isset($modules)) {
            return $modules;
        }

        $dispatcher = Factory::getApplication()->getDispatcher();
        $modules    = [];

        $modules =
$dispatcher->dispatch('onPrepareModuleList', new
Module\PrepareModuleListEvent('onPrepareModuleList', [
            'modules' => &$modules, // @todo: Remove
reference in Joomla 6, see PrepareModuleListEvent::__constructor()
        ]))->getArgument('modules', $modules);

        // If the onPrepareModuleList event returns an array of modules,
then ignore the default module list creation
        if (!$modules) {
            $modules = static::getModuleList();
        }

        $modules = $dispatcher->dispatch('onAfterModuleList',
new Module\AfterModuleListEvent('onAfterModuleList', [
            'modules' => &$modules, // @todo: Remove
reference in Joomla 6, see AfterModuleListEvent::__constructor()
        ]))->getArgument('modules', $modules);

        $modules = static::cleanModuleList($modules);

        $modules =
$dispatcher->dispatch('onAfterCleanModuleList', new
Module\AfterCleanModuleListEvent('onAfterCleanModuleList', [
            'modules' => &$modules, // @todo: Remove
reference in Joomla 6, see AfterCleanModuleListEvent::__constructor()
        ]))->getArgument('modules', $modules);

        return $modules;
    }

    /**
     * Module list
     *
     * @return  array
     */
    public static function getModuleList()
    {
        $app      = Factory::getApplication();
        $itemId   = $app->getInput()->getInt('Itemid', 0);
        $groups   = $app->getIdentity()->getAuthorisedViewLevels();
        $clientId = (int) $app->getClientId();

        // Build a cache ID for the resulting data object
        $cacheId = implode(',', $groups) . '.' .
$clientId . '.' . $itemId;

        $db      = Factory::getDbo();
        $query   = $db->getQuery(true);
        $nowDate = Factory::getDate()->toSql();

        $query->select($db->quoteName(['m.id',
'm.title', 'm.module', 'm.position',
'm.content', 'm.showtitle', 'm.params',
'mm.menuid']))
            ->from($db->quoteName('#__modules',
'm'))
            ->join(
                'LEFT',
                $db->quoteName('#__modules_menu',
'mm'),
                $db->quoteName('mm.moduleid') . ' =
' . $db->quoteName('m.id')
            )
            ->join(
                'LEFT',
                $db->quoteName('#__extensions',
'e'),
                $db->quoteName('e.element') . ' = '
. $db->quoteName('m.module')
                . ' AND ' .
$db->quoteName('e.client_id') . ' = ' .
$db->quoteName('m.client_id')
            )
            ->where(
                [
                    $db->quoteName('m.published') . ' =
1',
                    $db->quoteName('e.enabled') . ' =
1',
                    $db->quoteName('m.client_id') . ' =
:clientId',
                ]
            )
            ->bind(':clientId', $clientId,
ParameterType::INTEGER)
            ->whereIn($db->quoteName('m.access'), $groups)
            ->extendWhere(
                'AND',
                [
                    $db->quoteName('m.publish_up') . ' IS
NULL',
                    $db->quoteName('m.publish_up') . '
<= :publishUp',
                ],
                'OR'
            )
            ->bind(':publishUp', $nowDate)
            ->extendWhere(
                'AND',
                [
                    $db->quoteName('m.publish_down') . '
IS NULL',
                    $db->quoteName('m.publish_down') . '
>= :publishDown',
                ],
                'OR'
            )
            ->bind(':publishDown', $nowDate)
            ->extendWhere(
                'AND',
                [
                    $db->quoteName('mm.menuid') . ' =
:itemId',
                    $db->quoteName('mm.menuid') . ' <=
0',
                ],
                'OR'
            )
            ->bind(':itemId', $itemId,
ParameterType::INTEGER);

        // Filter by language
        if ($app->isClient('site') &&
$app->getLanguageFilter() ||
$app->isClient('administrator') &&
static::isAdminMultilang()) {
            $language = $app->getLanguage()->getTag();

            $query->whereIn($db->quoteName('m.language'),
[$language, '*'], ParameterType::STRING);
            $cacheId .= $language . '*';
        }

        $query->order($db->quoteName(['m.position',
'm.ordering']));

        // Set the query
        $db->setQuery($query);

        try {
            /** @var CallbackController $cache */
            $cache =
Factory::getContainer()->get(CacheControllerFactoryInterface::class)
                ->createCacheController('callback',
['defaultgroup' => 'com_modules']);

            $modules = $cache->get([$db, 'loadObjectList'],
[], md5($cacheId), false);
        } catch (\RuntimeException $e) {
            $app->getLogger()->warning(
               
Text::sprintf('JLIB_APPLICATION_ERROR_MODULE_LOAD',
$e->getMessage()),
                ['category' => 'jerror']
            );

            return [];
        }

        return $modules;
    }

    /**
     * Clean the module list
     *
     * @param   array  $modules  Array with module objects
     *
     * @return  array
     */
    public static function cleanModuleList($modules)
    {
        // Apply negative selections and eliminate duplicates
        $Itemid =
Factory::getApplication()->getInput()->getInt('Itemid');
        $negId  = $Itemid ? -(int) $Itemid : false;
        $clean  = [];
        $dupes  = [];

        foreach ($modules as $i => $module) {
            // The module is excluded if there is an explicit prohibition
            $negHit = ($negId === (int) $module->menuid);

            if (isset($dupes[$module->id])) {
                // If this item has been excluded, keep the duplicate flag
set,
                // but remove any item from the modules array.
                if ($negHit) {
                    unset($clean[$module->id]);
                }

                continue;
            }

            $dupes[$module->id] = true;

            // Only accept modules without explicit exclusions.
            if ($negHit) {
                continue;
            }

            $module->name     = substr($module->module, 4);
            $module->style    = null;
            $module->position = strtolower($module->position);

            $clean[$module->id] = $module;
        }

        unset($dupes);

        // Return to simple indexing that matches the query order.
        return array_values($clean);
    }

    /**
     * Module cache helper
     *
     * Caching modes:
     * To be set in XML:
     * 'static'      One cache file for all pages with the same
module parameters
     * 'itemid'      Changes on itemid change, to be called from
inside the module:
     * 'safeuri'     Id created from $cacheparams->modeparams
array,
     * 'id'          Module sets own cache id's
     *
     * @param   object  $module        Module object
     * @param   object  $moduleparams  Module parameters
     * @param   object  $cacheparams   Module cache parameters - id or URL
parameters, depending on the module cache mode
     *
     * @return  string
     *
     * @see     InputFilter::clean()
     * @since   1.6
     */
    public static function moduleCache($module, $moduleparams,
$cacheparams)
    {
        if (!isset($cacheparams->modeparams)) {
            $cacheparams->modeparams = null;
        }

        if (!isset($cacheparams->cachegroup)) {
            $cacheparams->cachegroup = $module->module;
        }

        if (!isset($cacheparams->cachesuffix)) {
            $cacheparams->cachesuffix = '';
        }

        $user = Factory::getUser();
        $app  = Factory::getApplication();

        /** @var CallbackController $cache */
        $cache =
Factory::getContainer()->get(CacheControllerFactoryInterface::class)
            ->createCacheController('callback',
['defaultgroup' => $cacheparams->cachegroup]);

        // Turn cache off for internal callers if parameters are set to off
and for all logged in users
        $ownCacheDisabled = $moduleparams->get('owncache') ===
0 || $moduleparams->get('owncache') === '0';
        $cacheDisabled    = $moduleparams->get('cache') === 0
|| $moduleparams->get('cache') === '0';

        if ($ownCacheDisabled || $cacheDisabled ||
$app->get('caching') == 0 || $user->get('id')) {
            $cache->setCaching(false);
        }

        // Module cache is set in seconds, global cache in minutes,
setLifeTime works in minutes
       
$cache->setLifeTime($moduleparams->get('cache_time',
$app->get('cachetime') * 60) / 60);

        $wrkaroundoptions = ['nopathway' => 1,
'nohead' => 0, 'nomodules' => 1,
'modulemode' => 1, 'mergehead' => 1];

        $wrkarounds  = true;
        $view_levels = md5(serialize($user->getAuthorisedViewLevels()));

        switch ($cacheparams->cachemode) {
            case 'id':
                $ret = $cache->get(
                    [$cacheparams->class, $cacheparams->method],
                    $cacheparams->methodparams,
                    $cacheparams->modeparams .
$cacheparams->cachesuffix,
                    $wrkarounds,
                    $wrkaroundoptions
                );
                break;

            case 'safeuri':
                $safeuri = new \stdClass();

                if (\is_array($cacheparams->modeparams)) {
                    $input        = $app->getInput();
                    $uri          = $input->getArray();
                    $noHtmlFilter = InputFilter::getInstance();

                    foreach ($cacheparams->modeparams as $key =>
$value) {
                        // Use int filter for id/catid to clean out spamy
slugs
                        if (isset($uri[$key])) {
                            $safeuri->$key =
$noHtmlFilter->clean($uri[$key], $value);
                        }
                    }
                }

                $secureid = md5(serialize([$safeuri,
$cacheparams->method, $moduleparams]));
                $ret      = $cache->get(
                    [$cacheparams->class, $cacheparams->method],
                    $cacheparams->methodparams,
                    $module->id . $view_levels . $secureid .
$cacheparams->cachesuffix,
                    $wrkarounds,
                    $wrkaroundoptions
                );
                break;

            case 'static':
                $ret = $cache->get(
                    [$cacheparams->class, $cacheparams->method],
                    $cacheparams->methodparams,
                    $module->module .
md5(serialize($cacheparams->methodparams)) .
$cacheparams->cachesuffix,
                    $wrkarounds,
                    $wrkaroundoptions
                );
                break;

            case 'itemid':
            default:
                $ret = $cache->get(
                    [$cacheparams->class, $cacheparams->method],
                    $cacheparams->methodparams,
                    $module->id . $view_levels .
$app->getInput()->getInt('Itemid', null) .
$cacheparams->cachesuffix,
                    $wrkarounds,
                    $wrkaroundoptions
                );
                break;
        }

        return $ret;
    }

    /**
     * Method to determine if filtering by language is enabled in back-end
for modules.
     *
     * @return  boolean  True if enabled; false otherwise.
     *
     * @since   3.8.0
     */
    public static function isAdminMultilang()
    {
        static $enabled = false;

        if (\count(LanguageHelper::getInstalledLanguages(1)) > 1) {
            $enabled = (bool)
ComponentHelper::getParams('com_modules')->get('adminlangfilter',
0);
        }

        return $enabled;
    }

    /**
     * Get module by id
     *
     * @param   string  $id  The id of the module
     *
     * @return  \stdClass  The Module object
     *
     * @since   3.9.0
     */
    public static function &getModuleById($id)
    {
        $modules =& static::load();

        $total = \count($modules);

        for ($i = 0; $i < $total; $i++) {
            // Match the id of the module
            if ((string) $modules[$i]->id === $id) {
                // Found it
                return $modules[$i];
            }
        }

        // If we didn't find it, create a dummy object
        $result = static::createDummyModule();

        return $result;
    }

    /**
     * Method to create a dummy module.
     *
     * @return  \stdClass  The Module object
     *
     * @since   4.0.0
     */
    protected static function createDummyModule(): \stdClass
    {
        $module            = new \stdClass();
        $module->id        = 0;
        $module->title     = '';
        $module->module    = '';
        $module->position  = '';
        $module->content   = '';
        $module->showtitle = 0;
        $module->control   = '';
        $module->params    = '';

        return $module;
    }
}
PKlE�[&���!�!ContentHelper.phpnu�[���<?php

/**
 * Joomla! Content Management System
 *
 * @copyright  (C) 2013 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license    GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\CMS\Helper;

use Joomla\CMS\Access\Access;
use Joomla\CMS\Application\ApplicationHelper;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\LanguageHelper;
use Joomla\CMS\Language\Multilanguage;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Log\Log;
use Joomla\CMS\MVC\View\CanDo;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Table\Table;
use Joomla\Registry\Registry;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Helper for standard content style extensions.
 * This class mainly simplifies static helper methods often repeated in
individual components
 *
 * @since  3.1
 */
class ContentHelper
{
    /**
     * Configure the Linkbar. Must be implemented by each extension.
     *
     * @param   string  $vName  The name of the active view.
     *
     * @return  void
     *
     * @since   3.1
     */
    public static function addSubmenu($vName)
    {
    }

    /**
     * Adds Count relations for Category and Tag Managers
     *
     * @param   \stdClass[]  &$items  The category or tag objects
     * @param   \stdClass    $config  Configuration object allowing to use
a custom relations table
     *
     * @return  \stdClass[]
     *
     * @since   3.9.1
     */
    public static function countRelations(&$items, $config)
    {
        $db = Factory::getDbo();

        // Allow custom state / condition values and custom column names to
support custom components
        $counter_names = $config->counter_names ?? [
            '-2' => 'count_trashed',
            '0'  => 'count_unpublished',
            '1'  => 'count_published',
            '2'  => 'count_archived',
        ];

        // Index category objects by their ID
        $records = [];

        foreach ($items as $item) {
            $records[(int) $item->id] = $item;
        }

        // The relation query does not return a value for cases without
relations of a particular state / condition, set zero as default
        foreach ($items as $item) {
            foreach ($counter_names as $n) {
                $item->{$n} = 0;
            }
        }

        // Table alias for related data table below will be 'c',
and state / condition column is inside related data table
        $related_tbl = '#__' . $config->related_tbl;
        $state_col   = 'c.' . $config->state_col;

        // Supported cases
        switch ($config->relation_type) {
            case 'tag_assigments':
                $recid_col = 'ct.' . $config->group_col;

                $query = $db->getQuery(true)
                   
->from($db->quoteName('#__contentitem_tag_map',
'ct'))
                    ->join(
                        'INNER',
                        $db->quoteName($related_tbl, 'c'),
                        $db->quoteName('ct.content_item_id') .
' = ' . $db->quoteName('c.id')
                        . ' AND ' .
$db->quoteName('ct.type_alias') . ' = :extension'
                    )
                    ->bind(':extension',
$config->extension);
                break;

            case 'category_or_group':
                $recid_col = 'c.' . $config->group_col;

                $query = $db->getQuery(true)
                    ->from($db->quoteName($related_tbl,
'c'));
                break;

            default:
                return $items;
        }

        /**
         * Get relation counts for all category objects with single query
         * NOTE: 'state IN', allows counting specific states /
conditions only, also prevents warnings with custom states / conditions, do
not remove
         */
        $query
            ->select(
                [
                    $db->quoteName($recid_col, 'catid'),
                    $db->quoteName($state_col, 'state'),
                    'COUNT(*) AS ' .
$db->quoteName('count'),
                ]
            )
            ->whereIn($db->quoteName($recid_col),
array_keys($records))
            ->whereIn($db->quoteName($state_col),
array_keys($counter_names))
            ->group($db->quoteName([$recid_col, $state_col]));

        $relationsAll = $db->setQuery($query)->loadObjectList();

        // Loop through the DB data overwriting the above zeros with the
found count
        foreach ($relationsAll as $relation) {
            // Sanity check in case someone removes the state IN above ...
and some views may start throwing warnings
            if (isset($counter_names[$relation->state])) {
                $id = (int) $relation->catid;
                $cn = $counter_names[$relation->state];

                $records[$id]->{$cn} = $relation->count;
            }
        }

        return $items;
    }

    /**
     * Gets a list of the actions that can be performed.
     *
     * @param   string   $component  The component name.
     * @param   string   $section    The access section name.
     * @param   integer  $id         The item ID.
     *
     * @return  Registry
     *
     * @since   3.2
     */
    public static function getActions($component = '', $section =
'', $id = 0)
    {
        $assetName = $component;

        if ($section && $id) {
            $assetName .= '.' . $section . '.' . (int)
$id;
        }

        // Return a CanDo object to prevent any BC break, will be changed
in 7.0 to Registry
        $result = new CanDo();

        $user = Factory::getUser();

        $actions = Access::getActionsFromFile(
            JPATH_ADMINISTRATOR . '/components/' . $component .
'/access.xml',
            '/access/section[@name="component"]/'
        );

        if ($actions === false) {
            Log::add(
               
Text::sprintf('JLIB_ERROR_COMPONENTS_ACL_CONFIGURATION_FILE_MISSING_OR_IMPROPERLY_STRUCTURED',
$component),
                Log::ERROR,
                'jerror'
            );

            return $result;
        }

        foreach ($actions as $action) {
            $result->set($action->name,
$user->authorise($action->name, $assetName));
        }

        return $result;
    }

    /**
     * Gets the current language
     *
     * @param   boolean  $detectBrowser  Flag indicating whether to use the
browser language as a fallback.
     *
     * @return  string  The language string
     *
     * @since   3.1
     * @note    CmsHelper::getCurrentLanguage is the preferred method
     */
    public static function getCurrentLanguage($detectBrowser = true)
    {
        $app      = Factory::getApplication();
        $langCode = null;

        // Get the languagefilter parameters
        if (Multilanguage::isEnabled()) {
            $plugin       = PluginHelper::getPlugin('system',
'languagefilter');
            $pluginParams = new Registry($plugin->params);

            if ((int) $pluginParams->get('lang_cookie', 1) ===
1) {
                $langCode =
$app->getInput()->cookie->getString(ApplicationHelper::getHash('language'));
            } else {
                $langCode =
$app->getSession()->get('plg_system_languagefilter.language');
            }
        }

        // No cookie - let's try to detect browser language or use
site default
        if (!$langCode) {
            if ($detectBrowser) {
                $langCode = LanguageHelper::detectLanguage();
            } else {
                $langCode =
ComponentHelper::getParams('com_languages')->get('site',
'en-GB');
            }
        }

        return $langCode;
    }

    /**
     * Gets the associated language ID
     *
     * @param   string  $langCode  The language code to look up
     *
     * @return  integer  The language ID
     *
     * @since   3.1
     * @note    CmsHelper::getLanguage() is the preferred method.
     */
    public static function getLanguageId($langCode)
    {
        $db    = Factory::getDbo();
        $query = $db->getQuery(true)
            ->select($db->quoteName('lang_id'))
            ->from($db->quoteName('#__languages'))
            ->where($db->quoteName('lang_code') . ' =
:language')
            ->bind(':language', $langCode);
        $db->setQuery($query);

        return $db->loadResult();
    }

    /**
     * Gets a row of data from a table
     *
     * @param   Table  $table  Table instance for a row.
     *
     * @return  array  Associative array of all columns and values for a
row in a table.
     *
     * @since   3.1
     */
    public function getRowData(Table $table)
    {
        $data = new CMSHelper();

        return $data->getRowData($table);
    }
}
PKlE�[k����AuthenticationHelper.phpnu�[���<?php

/**
 * Joomla! Content Management System
 *
 * @copyright  (C) 2016 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license    GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\CMS\Helper;

use Joomla\CMS\Event\User\LoginButtonsEvent;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Authentication helper class
 *
 * @since  3.6.3
 */
abstract class AuthenticationHelper
{
    /**
     * No longer used
     *
     * @return  array  Always empty
     *
     * @since   3.6.3
     *
     * @deprecated  4.2 will be removed in 6.0
     *              Will be removed without replacement
     */
    public static function getTwoFactorMethods()
    {
        return [];
    }

    /**
     * Get additional login buttons to add in a login module. These buttons
can be used for
     * authentication methods external to Joomla such as WebAuthn, login
with social media
     * providers, login with third party providers or even login with third
party Single Sign On
     * (SSO) services.
     *
     * Button definitions are returned by the onUserLoginButtons event
handlers in plugins. By
     * default, only system and user plugins are taken into account. The
former because they are
     * always loaded. The latter are explicitly loaded in this method.
     *
     * The onUserLoginButtons event handlers must conform to the following
method definition:
     *
     * public function onUserLoginButtons(string $formId): array
     *
     * The onUserLoginButtons event handlers must return a simple array
containing 0 or more button
     * definitions.
     *
     * Each button definition is a hash array with the following keys:
     *
     * * `label`   The translation string used as the label and title of
the button. Required
     * * `id`      The HTML ID of the button. Required.
     * * `tooltip` (optional) The translation string used as the alt tag of
the button's image
     * * `onclick` (optional) The onclick attribute, used to fire a
JavaScript event. Not
     *             recommended.
     * * `data-*`  (optional) Data attributes to pass verbatim. Use these
and JavaScript to handle
     *             the button.
     * * `icon`    (optional) A CSS class for an optional icon displayed
before the label; has
     *             precedence over 'image'
     * * `image`   (optional) An image path for an optional icon displayed
before the label
     * * `class`   (optional) CSS class(es) to be added to the button
     *
     * You can find a real world implementation of the onUserLoginButtons
plugin event in the
     * system/webauthn plugin.
     *
     * You can find a real world implementation of consuming the output of
this method in the
     * modules/mod_login module.
     *
     * Third party developers implementing a login module or a login form
in their component are
     * strongly advised to call this method and consume its results to
display additional login
     * buttons. Not doing that means that you are not fully compatible with
Joomla 4.
     *
     * @param   string  $formId  The HTML ID of the login form container.
Use it to filter when and
     *                           where to show your additional login
button(s)
     *
     * @return  array  Button definitions.
     *
     * @since   4.0.0
     */
    public static function getLoginButtons(string $formId): array
    {
        try {
            // Get all the User plugins.
            $dispatcher = Factory::getApplication()->getDispatcher();
            PluginHelper::importPlugin('user', null, true,
$dispatcher);
        } catch (\Exception $e) {
            return [];
        }

        // Trigger the onUserLoginButtons event and return the button
definitions.
        $btnEvent = new LoginButtonsEvent('onUserLoginButtons',
['subject' => $formId]);
        $dispatcher->dispatch('onUserLoginButtons',
$btnEvent);
        $results = $btnEvent['result'] ?? [];
        $buttons = [];

        foreach ($results as $result) {
            // Did we get garbage back from the plugin?
            if (!\is_array($result) || empty($result)) {
                continue;
            }

            // Did the developer accidentally return a single button
definition instead of an array?
            if (\array_key_exists('label', $result)) {
                $result = [$result];
            }

            // Process each button, making sure it conforms to the required
definition
            foreach ($result as $item) {
                // Force mandatory fields
                $defaultButtonDefinition = [
                    'label'   => '',
                    'tooltip' => '',
                    'icon'    => '',
                    'image'   => '',
                    'class'   => '',
                    'id'      => '',
                    'onclick' => '',
                ];

                $button = array_merge($defaultButtonDefinition, $item);

                // Unset anything that doesn't conform to a button
definition
                foreach (array_keys($button) as $key) {
                    if (substr($key, 0, 5) == 'data-') {
                        continue;
                    }

                    if (!\in_array($key, ['label',
'tooltip', 'icon', 'image', 'svg',
'class', 'id', 'onclick'])) {
                        unset($button[$key]);
                    }
                }

                // We need a label and an ID as the bare minimum
                if (empty($button['label']) ||
empty($button['id'])) {
                    continue;
                }

                $buttons[] = $button;
            }
        }

        return $buttons;
    }
}
PKlE�[AG::HelperFactoryAwareTrait.phpnu�[���<?php

/**
 * Joomla! Content Management System
 *
 * @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\CMS\Helper;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Defines the trait for a HelperFactory Aware Class.
 *
 * @since  4.2.0
 */
trait HelperFactoryAwareTrait
{
    /**
     * HelperFactory
     *
     * @var    HelperFactory
     *
     * @since  4.2.0
     */
    private $helper;

    /**
     * Get the HelperFactory.
     *
     * @return  HelperFactory
     *
     * @since   4.2.0
     *
     * @throws  \UnexpectedValueException May be thrown if the
HelperFactory has not been set.
     */
    public function getHelperFactory(): HelperFactory
    {
        if ($this->helper) {
            return $this->helper;
        }

        throw new \UnexpectedValueException('HelperFactory not set in
' . __CLASS__);
    }

    /**
     * Sets the helper factory to use.
     *
     * @param   HelperFactory  $helperFactory  The helper factory to use.
     *
     * @return  void
     *
     * @since   4.2.0
     */
    public function setHelperFactory(HelperFactory $helperFactory)
    {
        $this->helper = $helperFactory;
    }
}
PKlE�[����UserGroupsHelper.phpnu�[���<?php

/**
 * Joomla! Content Management System
 *
 * @copyright  (C) 2016 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license    GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\CMS\Helper;

use Joomla\CMS\Factory;
use Joomla\Database\ParameterType;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Helper to deal with user groups.
 *
 * @since  3.6.3
 */
final class UserGroupsHelper
{
    /**
     * Indicates the current helper instance is the singleton instance.
     *
     * @var    integer
     * @since  3.6.3
     */
    public const MODE_SINGLETON = 1;

    /**
     * Indicates the current helper instance is a standalone class
instance.
     *
     * @var    integer
     * @since  3.6.3
     */
    public const MODE_INSTANCE = 2;

    /**
     * Singleton instance.
     *
     * @var    UserGroupsHelper
     * @since  3.6.3
     */
    private static $instance;

    /**
     * Available user groups
     *
     * @var    array
     * @since  3.6.3
     */
    private $groups = [];

    /**
     * Mode this class is working: singleton or std instance
     *
     * @var    integer
     * @since  3.6.3
     */
    private $mode;

    /**
     * Total available groups
     *
     * @var    integer
     * @since  3.6.3
     */
    private $total;

    /**
     * Constructor
     *
     * @param   array    $groups  Array of groups
     * @param   integer  $mode    Working mode for this class
     *
     * @since   3.6.3
     */
    public function __construct(array $groups = [], $mode =
self::MODE_INSTANCE)
    {
        $this->mode = (int) $mode;

        if ($groups) {
            $this->setGroups($groups);
        }
    }

    /**
     * Count loaded user groups.
     *
     * @return  integer
     *
     * @since   3.6.3
     */
    public function count()
    {
        return \count($this->groups);
    }

    /**
     * Get the helper instance.
     *
     * @return  self
     *
     * @since   3.6.3
     */
    public static function getInstance()
    {
        if (static::$instance === null) {
            // Only here to avoid code style issues...
            $groups = [];

            static::$instance = new static($groups,
static::MODE_SINGLETON);
        }

        return static::$instance;
    }

    /**
     * Get a user group by its id.
     *
     * @param   integer  $id  Group identifier
     *
     * @return  mixed  stdClass on success. False otherwise
     *
     * @since   3.6.3
     */
    public function get($id)
    {
        if ($this->has($id)) {
            return $this->groups[$id];
        }

        // Singleton will load groups as they are requested
        if ($this->isSingleton()) {
            $this->groups[$id] = $this->load($id);

            return $this->groups[$id];
        }

        return false;
    }

    /**
     * Get the list of existing user groups.
     *
     * @return  array
     *
     * @since   3.6.3
     */
    public function getAll()
    {
        if ($this->isSingleton() && $this->total() !==
$this->count()) {
            $this->loadAll();
        }

        return $this->groups;
    }

    /**
     * Check if a group is in the list.
     *
     * @param   integer  $id  Group identifier
     *
     * @return  boolean
     *
     * @since   3.6.3
     */
    public function has($id)
    {
        return (\array_key_exists($id, $this->groups) &&
$this->groups[$id] !== false);
    }

    /**
     * Check if this instance is a singleton.
     *
     * @return  boolean
     *
     * @since   3.6.3
     */
    private function isSingleton()
    {
        return $this->mode === static::MODE_SINGLETON;
    }

    /**
     * Get total available user groups in database.
     *
     * @return  integer
     *
     * @since   3.6.3
     */
    public function total()
    {
        if ($this->total === null) {
            $db = Factory::getDbo();

            $query = $db->getQuery(true)
                ->select('COUNT(' .
$db->quoteName('id') . ')')
                ->from($db->quoteName('#__usergroups'));

            $db->setQuery($query);

            $this->total = (int) $db->loadResult();
        }

        return $this->total;
    }

    /**
     * Load a group from database.
     *
     * @param   integer  $id  Group identifier
     *
     * @return  mixed
     *
     * @since   3.6.3
     */
    public function load($id)
    {
        // Cast as integer until method is typehinted.
        $id = (int) $id;

        $db = Factory::getDbo();

        $query = $db->getQuery(true)
            ->select('*')
            ->from($db->quoteName('#__usergroups'))
            ->where($db->quoteName('id') . ' =
:id')
            ->bind(':id', $id, ParameterType::INTEGER);

        $db->setQuery($query);

        $group = $db->loadObject();

        if (!$group) {
            return false;
        }

        return $this->populateGroupData($group);
    }

    /**
     * Load all user groups from the database.
     *
     * @return  self
     *
     * @since   3.6.3
     */
    public function loadAll()
    {
        $this->groups = [];

        $db = Factory::getDbo();

        $query = $db->getQuery(true)
            ->select('*')
            ->from($db->quoteName('#__usergroups'))
            ->order($db->quoteName('lft') . '
ASC');

        $db->setQuery($query);

        $groups = $db->loadObjectList('id');

        $this->groups = $groups ?: [];
        $this->populateGroupsData();

        return $this;
    }

    /**
     * Populates extra information for groups.
     *
     * @return  array
     *
     * @since   3.6.3
     */
    private function populateGroupsData()
    {
        foreach ($this->groups as $group) {
            $this->populateGroupData($group);
        }

        return $this->groups;
    }

    /**
     * Populate data for a specific user group.
     *
     * @param   \stdClass  $group  Group
     *
     * @return  \stdClass
     *
     * @since   3.6.3
     */
    public function populateGroupData($group)
    {
        if (!$group || property_exists($group, 'path')) {
            return $group;
        }

        $parentId = (int) $group->parent_id;

        if ($parentId === 0) {
            $group->path  = [$group->id];
            $group->level = 0;

            return $group;
        }

        $parentGroup = $this->has($parentId) ? $this->get($parentId)
: $this->load($parentId);

        if (!property_exists($parentGroup, 'path')) {
            $parentGroup = $this->populateGroupData($parentGroup);
        }

        $group->path  = array_merge($parentGroup->path,
[$group->id]);
        $group->level = \count($group->path) - 1;

        return $group;
    }

    /**
     * Set the groups to be used as source.
     *
     * @param   array  $groups  Array of user groups.
     *
     * @return  self
     *
     * @since   3.6.3
     */
    public function setGroups(array $groups)
    {
        $this->groups = $groups;
        $this->populateGroupsData();
        $this->total  = \count($groups);

        return $this;
    }
}
PKlE�[��D���
CMSHelper.phpnu�[���<?php

/**
 * Joomla! Content Management System
 *
 * @copyright  (C) 2013 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license    GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\CMS\Helper;

use Joomla\CMS\Application\ApplicationHelper;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\LanguageHelper;
use Joomla\CMS\Language\Multilanguage;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Table\TableInterface;
use Joomla\Registry\Registry;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Base Helper class.
 *
 * @since  3.2
 */
class CMSHelper
{
    /**
     * Gets the current language
     *
     * @param   boolean  $detectBrowser  Flag indicating whether to use the
browser language as a fallback.
     *
     * @return  string  The language string
     *
     * @since   3.2
     */
    public function getCurrentLanguage($detectBrowser = true)
    {
        $app      = Factory::getApplication();
        $langCode = null;

        // Get the languagefilter parameters
        if (Multilanguage::isEnabled()) {
            $plugin       = PluginHelper::getPlugin('system',
'languagefilter');
            $pluginParams = new Registry($plugin->params);

            if ((int) $pluginParams->get('lang_cookie', 1) ===
1) {
                $langCode =
$app->getInput()->cookie->getString(ApplicationHelper::getHash('language'));
            } else {
                $langCode =
$app->getSession()->get('plg_system_languagefilter.language');
            }
        }

        // No cookie - let's try to detect browser language or use
site default
        if (!$langCode) {
            if ($detectBrowser) {
                $langCode = LanguageHelper::detectLanguage();
            } else {
                $langCode =
ComponentHelper::getParams('com_languages')->get('site',
'en-GB');
            }
        }

        return $langCode;
    }

    /**
     * Gets the associated language ID
     *
     * @param   string  $langCode  The language code to look up
     *
     * @return  integer  The language ID
     *
     * @since   3.2
     */
    public function getLanguageId($langCode)
    {
        $db    = Factory::getDbo();
        $query = $db->getQuery(true)
            ->select($db->quoteName('lang_id'))
            ->from($db->quoteName('#__languages'))
            ->where($db->quoteName('lang_code') . ' =
:language')
            ->bind(':language', $langCode);
        $db->setQuery($query);

        return $db->loadResult();
    }

    /**
     * Gets a row of data from a table
     *
     * @param   TableInterface  $table  Table instance for a row.
     *
     * @return  array  Associative array of all columns and values for a
row in a table.
     *
     * @since   3.2
     */
    public function getRowData(TableInterface $table)
    {
        $fields = $table->getFields();
        $data   = [];

        foreach ($fields as &$field) {
            $columnName        = $field->Field;
            $value             = $table->$columnName;
            $data[$columnName] = $value;
        }

        return $data;
    }

    /**
     * Method to get an object containing all of the table columns and
values.
     *
     * @param   TableInterface  $table  Table object.
     *
     * @return  \stdClass  Contains all of the columns and values.
     *
     * @since   3.2
     */
    public function getDataObject(TableInterface $table)
    {
        $fields     = $table->getFields();
        $dataObject = new \stdClass();

        foreach ($fields as $field) {
            $fieldName              = $field->Field;
            $dataObject->$fieldName = $table->$fieldName ?? null;
        }

        return $dataObject;
    }
}
PKlE�[(�\�EFEFMediaHelper.phpnu�[���<?php

/**
 * Joomla! Content Management System
 *
 * @copyright  (C) 2013 Open Source Matters, Inc.
<https://www.joomla.org>
 * @license    GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\CMS\Helper;

use enshrined\svgSanitize\Sanitizer;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Filesystem\File;
use Joomla\CMS\Filter\InputFilter;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\Registry\Registry;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Media helper class
 *
 * @since  3.2
 */
class MediaHelper
{
    /**
     * A special list of blocked executable extensions, skipping
executables that are
     * typically executable in the webserver context as those are fetched
from
     * Joomla\CMS\Filter\InputFilter
     *
     * @var    string[]
     * @since  4.0.0
     */
    public const EXECUTABLES = [
        'js', 'exe', 'dll', 'go',
'ade', 'adp', 'bat', 'chm',
'cmd', 'com', 'cpl', 'hta',
        'ins', 'isp', 'jse', 'lib',
'mde', 'msc', 'msp', 'mst',
'pif', 'scr', 'sct', 'shb',
        'sys', 'vb', 'vbe', 'vbs',
'vxd', 'wsc', 'wsf', 'wsh',
'html', 'htm', 'msi',
    ];

    /**
     * Checks if the file is an image
     *
     * @param   string  $fileName  The filename
     *
     * @return  boolean
     *
     * @since   3.2
     */
    public static function isImage($fileName)
    {
        static $imageTypes =
'xcf|odg|gif|jpg|jpeg|png|bmp|webp|avif';

        return preg_match("/\.(?:$imageTypes)$/i", $fileName);
    }

    /**
     * Gets the file extension for purposed of using an icon
     *
     * @param   string  $fileName  The filename
     *
     * @return  string  File extension to determine icon
     *
     * @since   3.2
     */
    public static function getTypeIcon($fileName)
    {
        return strtolower(substr($fileName, strrpos($fileName,
'.') + 1));
    }

    /**
     * Get the Mime type
     *
     * @param   string   $file     The link to the file to be checked
     * @param   boolean  $isImage  True if the passed file is an image else
false
     *
     * @return  mixed    the mime type detected false on error
     *
     * @since   3.7.2
     */
    public static function getMimeType($file, $isImage = false)
    {
        // If we can't detect anything mime is false
        $mime = false;

        try {
            if ($isImage &&
\function_exists('exif_imagetype')) {
                $mime = image_type_to_mime_type(exif_imagetype($file));
            } elseif ($isImage &&
\function_exists('getimagesize')) {
                $imagesize = getimagesize($file);
                $mime      = $imagesize['mime'] ?? false;
            } elseif (\function_exists('mime_content_type')) {
                // We have mime magic.
                $mime = mime_content_type($file);
            } elseif (\function_exists('finfo_open')) {
                // We have fileinfo
                $finfo = finfo_open(FILEINFO_MIME_TYPE);
                $mime  = finfo_file($finfo, $file);
                finfo_close($finfo);
            }
        } catch (\Exception $e) {
            // If we have any kind of error here => false;
            return false;
        }

        // If we can't detect the mime try it again
        if ($mime === 'application/octet-stream' &&
$isImage === true) {
            $mime = static::getMimeType($file, false);
        }

        if (
            ($mime === 'application/octet-stream' || $mime ===
'image/svg' || $mime === 'image/svg+xml')
            && !$isImage && strtolower(pathinfo($file,
PATHINFO_EXTENSION)) === 'svg' && self::isValidSvg($file,
false)
        ) {
            return 'image/svg+xml';
        }

        // We have a mime here
        return $mime;
    }

    /**
     * Checks the Mime type
     *
     * @param   string  $mime       The mime to be checked
     * @param   string  $component  The optional name for the component
storing the parameters
     *
     * @return  boolean  true if mime type checking is disabled or it
passes the checks else false
     *
     * @since   3.7
     */
    private function checkMimeType($mime, $component =
'com_media'): bool
    {
        $params = ComponentHelper::getParams($component);

        if ($params->get('check_mime', 1)) {
            $allowedMime = $params->get(
                'upload_mime',
               
'image/jpeg,image/gif,image/png,image/bmp,image/webp,image/avif,application/msword,'
.
                   
'application/excel,application/pdf,application/powerpoint,text/plain,application/x-zip'
            );

            // Get the mime type configuration
            $allowedMime = array_map('trim',
explode(',', str_replace('\\', '',
$allowedMime)));

            // Mime should be available and in the allowed list
            return !empty($mime) && \in_array($mime, $allowedMime);
        }

        // We don't check mime at all or it passes the checks
        return true;
    }

    /**
     * Checks the file extension
     *
     * @param   string  $extension  The extension to be checked
     * @param   string  $component  The optional name for the component
storing the parameters
     *
     * @return  boolean  true if it passes the checks else false
     *
     * @since   4.0.0
     */
    public static function checkFileExtension($extension, $component =
'com_media', $allowedExecutables = []): bool
    {
        $params = ComponentHelper::getParams($component);

        // Media file names should never have executable extensions buried
in them.
        $executables = array_merge(self::EXECUTABLES,
InputFilter::FORBIDDEN_FILE_EXTENSIONS);

        // Remove allowed executables from array
        if (\count($allowedExecutables)) {
            $executables = array_diff($executables, $allowedExecutables);
        }

        if (\in_array($extension, $executables, true)) {
            return false;
        }

        $allowable = array_map('trim', explode(',',
$params->get('restrict_uploads_extensions',
'bmp,gif,jpg,jpeg,png,webp,avif,ico,mp3,m4a,mp4a,ogg,mp4,mp4v,mpeg,mov,odg,odp,ods,odt,pdf,ppt,txt,xcf,xls,csv')));
        $ignored   = array_map('trim', explode(',',
$params->get('ignore_extensions', '')));

        if ($extension == '' || $extension == false ||
(!\in_array($extension, $allowable, true) && !\in_array($extension,
$ignored, true))) {
            return false;
        }

        // We don't check mime at all or it passes the checks
        return true;
    }

    /**
     * Checks if the file can be uploaded
     *
     * @param   array     $file                File information
     * @param   string    $component           The option name for the
component storing the parameters
     * @param   string[]  $allowedExecutables  Array of executable file
types that shall be whitelisted
     *
     * @return  boolean
     *
     * @since   3.2
     */
    public function canUpload($file, $component = 'com_media',
$allowedExecutables = [])
    {
        $app    = Factory::getApplication();
        $params = ComponentHelper::getParams($component);

        if (empty($file['name'])) {
           
$app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_UPLOAD_INPUT'),
'error');

            return false;
        }

        if ($file['name'] !==
File::makeSafe($file['name'])) {
           
$app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNFILENAME'),
'error');

            return false;
        }

        $filetypes = explode('.', $file['name']);

        if (\count($filetypes) < 2) {
            // There seems to be no extension
           
$app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNFILETYPE'),
'error');

            return false;
        }

        array_shift($filetypes);

        // Media file names should never have executable extensions buried
in them.
        $executables = array_merge(self::EXECUTABLES,
InputFilter::FORBIDDEN_FILE_EXTENSIONS);

        // Remove allowed executables from array
        if (\count($allowedExecutables)) {
            $executables = array_diff($executables, $allowedExecutables);
        }

        // Ensure lowercase extension
        $filetypes = array_map('strtolower', $filetypes);

        $check = array_intersect($filetypes, $executables);

        if (!empty($check)) {
           
$app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNFILETYPE'),
'error');

            return false;
        }

        $filetype = array_pop($filetypes);

        $allowable = array_map('trim', explode(',',
$params->get('restrict_uploads_extensions',
'bmp,gif,jpg,jpeg,png,webp,avif,ico,mp3,m4a,mp4a,ogg,mp4,mp4v,mpeg,mov,odg,odp,ods,odt,pdf,png,ppt,txt,xcf,xls,csv')));
        $ignored   = array_map('trim', explode(',',
$params->get('ignore_extensions', '')));

        if ($filetype == '' || $filetype == false ||
(!\in_array($filetype, $allowable) && !\in_array($filetype,
$ignored))) {
           
$app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNFILETYPE'),
'error');

            return false;
        }

        $maxSize = (int) ($params->get('upload_maxsize', 0) *
1024 * 1024);

        if ($maxSize > 0 && (int) $file['size'] >
$maxSize) {
           
$app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNFILETOOLARGE'),
'error');

            return false;
        }

        if ($params->get('restrict_uploads', 1)) {
            $allowedExtensions = array_map('trim',
explode(',',
$params->get('restrict_uploads_extensions',
'bmp,gif,jpg,jpeg,png,webp,avif,ico,mp3,m4a,mp4a,ogg,mp4,mp4v,mpeg,mov,odg,odp,ods,odt,pdf,png,ppt,txt,xcf,xls,csv')));

            if (\in_array($filetype, $allowedExtensions)) {
                // If tmp_name is empty, then the file was bigger than the
PHP limit
                if (!empty($file['tmp_name'])) {
                    // Get the mime type this is an image file
                    $mime =
static::getMimeType($file['tmp_name'],
static::isImage($file['tmp_name']));

                    // Did we get anything useful?
                    if ($mime != false) {
                        $result = $this->checkMimeType($mime,
$component);

                        // If the mime type is not allowed we don't
upload it and show the mime code error to the user
                        if ($result === false) {
                           
$app->enqueueMessage(Text::sprintf('JLIB_MEDIA_ERROR_WARNINVALID_MIMETYPE',
$mime), 'error');

                            return false;
                        }
                    } else {
                        // We can't detect the mime type so it looks
like an invalid image
                       
$app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNINVALID_IMG'),
'error');

                        return false;
                    }
                } else {
                   
$app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNFILETOOLARGE'),
'error');

                    return false;
                }
            } elseif (!\in_array($filetype, $ignored)) {
                // Get the mime type this is not an image file
                $mime = static::getMimeType($file['tmp_name'],
false);

                // Did we get anything useful?
                if ($mime != false) {
                    $result = $this->checkMimeType($mime, $component);

                    // If the mime type is not allowed we don't upload
it and show the mime code error to the user
                    if ($result === false) {
                       
$app->enqueueMessage(Text::sprintf('JLIB_MEDIA_ERROR_WARNINVALID_MIMETYPE',
$mime), 'error');

                        return false;
                    }
                } else {
                    // We can't detect the mime type so it looks like
an invalid file
                   
$app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNINVALID_MIME'),
'error');

                    return false;
                }

                if
(!Factory::getUser()->authorise('core.manage', $component)) {
                   
$app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNNOTADMIN'),
'error');

                    return false;
                }
            }
        }

        if ($filetype === 'svg') {
            return self::isValidSvg($file['tmp_name'], true);
        }

        return true;
    }

    /**
     * Calculate the size of a resized image
     *
     * @param   integer  $width   Image width
     * @param   integer  $height  Image height
     * @param   integer  $target  Target size
     *
     * @return  array  The new width and height
     *
     * @since   3.2
     */
    public static function imageResize($width, $height, $target)
    {
        /*
         * Takes the larger size of the width and height and applies the
         * formula accordingly. This is so this script will work
         * dynamically with any size image
         */
        if ($width > $height) {
            $percentage = ($target / $width);
        } else {
            $percentage = ($target / $height);
        }

        // Gets the new value and applies the percentage, then rounds the
value
        $width  = round($width * $percentage);
        $height = round($height * $percentage);

        return [$width, $height];
    }

    /**
     * Counts the files and directories in a directory that are not php or
html files.
     *
     * @param   string  $dir  Directory name
     *
     * @return  array  The number of media files and directories in the
given directory
     *
     * @since   3.2
     */
    public function countFiles($dir)
    {
        $total_file = 0;
        $total_dir  = 0;

        if (is_dir($dir)) {
            $d = dir($dir);

            while (($entry = $d->read()) !== false) {
                if ($entry[0] !== '.' && strpos($entry,
'.html') === false && strpos($entry, '.php')
=== false && is_file($dir . DIRECTORY_SEPARATOR . $entry)) {
                    $total_file++;
                }

                if ($entry[0] !== '.' && is_dir($dir .
DIRECTORY_SEPARATOR . $entry)) {
                    $total_dir++;
                }
            }

            $d->close();
        }

        return [$total_file, $total_dir];
    }

    /**
     * Small helper function that properly converts any
     * configuration options to their byte representation.
     *
     * @param   string|integer  $val  The value to be converted to bytes.
     *
     * @return integer The calculated bytes value from the input.
     *
     * @since 3.3
     */
    public function toBytes($val)
    {
        switch ($val[\strlen($val) - 1]) {
            case 'M':
            case 'm':
                return (int) $val * 1048576;
            case 'K':
            case 'k':
                return (int) $val * 1024;
            case 'G':
            case 'g':
                return (int) $val * 1073741824;
            default:
                return $val;
        }
    }

    /**
     * Method to check if the given directory is a directory configured in
FileSystem - Local plugin
     *
     * @param   string  $directory
     *
     * @return  boolean
     *
     * @since   4.0.0
     */
    public static function isValidLocalDirectory($directory)
    {
        $plugin = PluginHelper::getPlugin('filesystem',
'local');

        if ($plugin) {
            $params = new Registry($plugin->params);

            $directories = $params->get('directories',
'[{"directory": "images"}]');

            // Do a check if default settings are not saved by user
            // If not initialize them manually
            if (\is_string($directories)) {
                $directories = json_decode($directories);
            }

            foreach ($directories as $directoryEntity) {
                if ($directoryEntity->directory === $directory) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Helper method get clean data for value stores in a Media form field
by removing adapter information
     * from the value if available (in this case, the value will have this
format:
     *
images/headers/blue-flower.jpg#joomlaImage://local-images/headers/blue-flower.jpg?width=700&height=180)
     *
     * @param   string  $value
     *
     * @return  string
     *
     * @since   4.0.0
     */
    public static function getCleanMediaFieldValue($value)
    {
        if ($pos = strpos($value, '#')) {
            return substr($value, 0, $pos);
        }

        return $value;
    }

    /**
     * Check if a file is a valid SVG
     *
     * @param  string  $file
     * @param  bool    $shouldLogErrors
     *
     * @return  boolean
     *
     * @since   4.3.0
     */
    private static function isValidSvg($file, $shouldLogErrors = true):
bool
    {
        $sanitizer = new Sanitizer();

        $isValid = $sanitizer->sanitize(file_get_contents($file));

        $svgErrors = $sanitizer->getXmlIssues();

        /**
         * We allow comments and temp fix for bugs in svg-santitizer
         * https://github.com/darylldoyle/svg-sanitizer/issues/64
         * https://github.com/darylldoyle/svg-sanitizer/issues/63
         * https://github.com/darylldoyle/svg-sanitizer/pull/65
         * https://github.com/darylldoyle/svg-sanitizer/issues/82
         */
        foreach ($svgErrors as $i => $error) {
            if (
                ($error['message'] === 'Suspicious node
\'#comment\'')
                || ($error['message'] === 'Suspicious
attribute \'space\'')
                || ($error['message'] === 'Suspicious
attribute \'enable-background\'')
                || ($error['message'] === 'Suspicious node
\'svg\'')
            ) {
                unset($svgErrors[$i]);
            }
        }

        if ($isValid === false || \count($svgErrors)) {
            if ($shouldLogErrors) {
               
Factory::getApplication()->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNIEXSS'),
'error');
            }

            return false;
        }

        return true;
    }
}
PKlE�[���S��HelperFactoryAwareInterface.phpnu�[���PKlE�[���((/LibraryHelper.phpnu�[���PKlE�[�o�3�3��TagsHelper.phpnu�[���PKlE�[��99	�PublicFolderGeneratorHelper.phpnu�[���PKlE�[�
�f����HelperFactoryInterface.phpnu�[���PKlE�[žȀTT��HelperFactory.phpnu�[���PKlE�[����!�!O�RouteHelper.phpnu�[���PKlE�[�Ư��`�`�ModuleHelper.phpnu�[���PKlE�[&���!�!�bContentHelper.phpnu�[���PKlE�[k������AuthenticationHelper.phpnu�[���PKlE�[AG::��HelperFactoryAwareTrait.phpnu�[���PKlE�[��Р���UserGroupsHelper.phpnu�[���PKlE�[��D���
;�CMSHelper.phpnu�[���PKlE�[(�\�EFEF^�MediaHelper.phpnu�[���PK��