Spade
Mini Shell
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��