first commit

This commit is contained in:
2025-06-17 11:53:18 +02:00
commit 9f0f7ba12b
8804 changed files with 1369176 additions and 0 deletions

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="actionlog" method="upgrade">
<name>plg_actionlog_joomla</name>
<author>Joomla! Project</author>
<creationDate>2018-05</creationDate>
<copyright>(C) 2018 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>3.9.0</version>
<description>PLG_ACTIONLOG_JOOMLA_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\Actionlog\Joomla</namespace>
<files>
<folder plugin="joomla">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_actionlog_joomla.ini</language>
<language tag="en-GB">language/en-GB/plg_actionlog_joomla.sys.ini</language>
</languages>
</extension>

View File

@ -0,0 +1,50 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Actionlog.joomla
*
* @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\User\UserFactoryInterface;
use Joomla\Database\DatabaseInterface;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Actionlog\Joomla\Extension\Joomla;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.2.0
*/
public function register(Container $container)
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new Joomla(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('actionlog', 'joomla')
);
$plugin->setApplication(Factory::getApplication());
$plugin->setDatabase($container->get(DatabaseInterface::class));
$plugin->setUserFactory($container->get(UserFactoryInterface::class));
return $plugin;
}
);
}
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="api-authentication" method="upgrade">
<name>plg_api-authentication_basic</name>
<author>Joomla! Project</author>
<creationDate>2005-11</creationDate>
<copyright>(C) 2019 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>4.0.0</version>
<description>PLG_API-AUTHENTICATION_BASIC_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\ApiAuthentication\Basic</namespace>
<files>
<folder plugin="basic">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_api-authentication_basic.ini</language>
<language tag="en-GB">language/en-GB/plg_api-authentication_basic.sys.ini</language>
</languages>
</extension>

View File

@ -0,0 +1,50 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Apiauthentication.basic
*
* @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\User\UserFactoryInterface;
use Joomla\Database\DatabaseInterface;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\ApiAuthentication\Basic\Extension\Basic;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.2.0
*/
public function register(Container $container)
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new Basic(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('api-authentication', 'basic'),
);
$plugin->setApplication(Factory::getApplication());
$plugin->setDatabase($container->get(DatabaseInterface::class));
$plugin->setUserFactory($container->get(UserFactoryInterface::class));
return $plugin;
}
);
}
};

View File

@ -0,0 +1,101 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Apiauthentication.basic
*
* @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Plugin\ApiAuthentication\Basic\Extension;
use Joomla\CMS\Authentication\Authentication;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\User\UserFactoryAwareTrait;
use Joomla\CMS\User\UserHelper;
use Joomla\Database\DatabaseAwareTrait;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Joomla Authentication plugin
*
* @since 4.0.0
*/
final class Basic extends CMSPlugin
{
use DatabaseAwareTrait;
use UserFactoryAwareTrait;
/**
* This method should handle any authentication and report back to the subject
*
* @param array $credentials Array holding the user credentials
* @param array $options Array of extra options
* @param object &$response Authentication response object
*
* @return void
*
* @since 4.0.0
*/
public function onUserAuthenticate($credentials, $options, &$response)
{
$response->type = 'Basic';
$username = $this->getApplication()->getInput()->server->get('PHP_AUTH_USER', '', 'USERNAME');
$password = $this->getApplication()->getInput()->server->get('PHP_AUTH_PW', '', 'RAW');
if ($password === '') {
$response->status = Authentication::STATUS_FAILURE;
$response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_EMPTY_PASS_NOT_ALLOWED');
return;
}
$db = $this->getDatabase();
$query = $db->getQuery(true)
->select($db->quoteName(['id', 'password']))
->from($db->quoteName('#__users'))
->where($db->quoteName('username') . ' = :username')
->bind(':username', $username);
$db->setQuery($query);
$result = $db->loadObject();
if ($result) {
$match = UserHelper::verifyPassword($password, $result->password, $result->id);
if ($match === true) {
// Bring this in line with the rest of the system
$user = $this->getUserFactory()->loadUserById($result->id);
$response->email = $user->email;
$response->fullname = $user->name;
$response->username = $username;
if ($this->getApplication()->isClient('administrator')) {
$response->language = $user->getParam('admin_language');
} else {
$response->language = $user->getParam('language');
}
$response->status = Authentication::STATUS_SUCCESS;
$response->error_message = '';
} else {
// Invalid password
$response->status = Authentication::STATUS_FAILURE;
$response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_INVALID_PASS');
}
} else {
// Let's hash the entered password even if we don't have a matching user for some extra response time
// By doing so, we mitigate side channel user enumeration attacks
UserHelper::hashPassword($password);
// Invalid user
$response->status = Authentication::STATUS_FAILURE;
$response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_NO_USER');
}
}
}

View File

@ -0,0 +1,52 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Apiauthentication.token
*
* @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\User\UserFactoryInterface;
use Joomla\Database\DatabaseInterface;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Filter\InputFilter;
use Joomla\Plugin\ApiAuthentication\Token\Extension\Token;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.2.0
*/
public function register(Container $container)
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new Token(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('api-authentication', 'token'),
new InputFilter()
);
$plugin->setApplication(Factory::getApplication());
$plugin->setDatabase($container->get(DatabaseInterface::class));
$plugin->setUserFactory($container->get(UserFactoryInterface::class));
return $plugin;
}
);
}
};

View File

@ -0,0 +1,396 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Apiauthentication.token
*
* @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Plugin\ApiAuthentication\Token\Extension;
use Joomla\CMS\Authentication\Authentication;
use Joomla\CMS\Crypt\Crypt;
use Joomla\CMS\Event\User\AuthenticationEvent;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\User\UserFactoryAwareTrait;
use Joomla\Component\Plugins\Administrator\Model\PluginModel;
use Joomla\Database\DatabaseAwareTrait;
use Joomla\Database\ParameterType;
use Joomla\Event\DispatcherInterface;
use Joomla\Event\SubscriberInterface;
use Joomla\Filter\InputFilter;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Joomla Token Authentication plugin
*
* @since 4.0.0
*/
final class Token extends CMSPlugin implements SubscriberInterface
{
use DatabaseAwareTrait;
use UserFactoryAwareTrait;
/**
* The prefix of the user profile keys, without the dot.
*
* @var string
* @since 4.0.0
*/
private $profileKeyPrefix = 'joomlatoken';
/**
* Allowed HMAC algorithms for the token
*
* @var string[]
* @since 4.0.0
*/
private $allowedAlgos = ['sha256', 'sha512'];
/**
* The input filter
*
* @var InputFilter
* @since 4.2.0
*/
private $filter;
/**
* Returns an array of events this subscriber will listen to.
*
* @return array
*
* @since 5.0.0
*/
public static function getSubscribedEvents(): array
{
return ['onUserAuthenticate' => 'onUserAuthenticate'];
}
/**
* Constructor.
*
* @param DispatcherInterface $dispatcher The dispatcher
* @param array $config An optional associative array of configuration settings
* @param InputFilter $filter The input filter
*
* @since 4.2.0
*/
public function __construct(DispatcherInterface $dispatcher, array $config, InputFilter $filter)
{
parent::__construct($dispatcher, $config);
$this->filter = $filter;
}
/**
* This method should handle any authentication and report back to the subject
*
* @param AuthenticationEvent $event Authentication event
*
* @return void
*
* @since 4.0.0
*/
public function onUserAuthenticate(AuthenticationEvent $event): void
{
$response = $event->getAuthenticationResponse();
// Default response is authentication failure.
$response->type = 'Token';
$response->status = Authentication::STATUS_FAILURE;
$response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_FAIL');
/**
* First look for an HTTP Authorization header with the following format:
* Authorization: Bearer <token>
* Do keep in mind that Bearer is **case-sensitive**. Whitespace between Bearer and the
* token, as well as any whitespace following the token is discarded.
*/
$authHeader = $this->getApplication()->getInput()->server->get('HTTP_AUTHORIZATION', '', 'string');
$tokenString = '';
// Apache specific fixes. See https://github.com/symfony/symfony/issues/19693
if (
empty($authHeader) && \PHP_SAPI === 'apache2handler'
&& \function_exists('apache_request_headers') && apache_request_headers() !== false
) {
$apacheHeaders = array_change_key_case(apache_request_headers(), CASE_LOWER);
if (\array_key_exists('authorization', $apacheHeaders)) {
$authHeader = $this->filter->clean($apacheHeaders['authorization'], 'STRING');
}
}
if (substr($authHeader, 0, 7) == 'Bearer ') {
$parts = explode(' ', $authHeader, 2);
$tokenString = trim($parts[1]);
$tokenString = $this->filter->clean($tokenString, 'BASE64');
}
if (empty($tokenString)) {
$tokenString = $this->getApplication()->getInput()->server->get('HTTP_X_JOOMLA_TOKEN', '', 'string');
}
// No token: authentication failure
if (empty($tokenString)) {
return;
}
// The token is a base64 encoded string. Make sure we can decode it.
$authString = @base64_decode($tokenString);
if (empty($authString) || (strpos($authString, ':') === false)) {
return;
}
/**
* Deconstruct the decoded token string to its three discrete parts: algorithm, user ID and
* HMAC of the token string saved in the database.
*/
$parts = explode(':', $authString, 3);
if (\count($parts) != 3) {
return;
}
list($algo, $userId, $tokenHMAC) = $parts;
/**
* Verify the HMAC algorithm requested in the token string is allowed
*/
$allowedAlgo = \in_array($algo, $this->allowedAlgos);
/**
* Make sure the user ID is an integer
*/
$userId = (int) $userId;
/**
* Calculate the reference token data HMAC
*/
try {
$siteSecret = $this->getApplication()->get('secret');
} catch (\Exception $e) {
return;
}
// An empty secret! What kind of monster are you?!
if (empty($siteSecret)) {
return;
}
$referenceTokenData = $this->getTokenSeedForUser($userId);
$referenceTokenData = empty($referenceTokenData) ? '' : $referenceTokenData;
$referenceTokenData = base64_decode($referenceTokenData);
$referenceHMAC = hash_hmac($algo, $referenceTokenData, $siteSecret);
// Is the token enabled?
$enabled = $this->isTokenEnabledForUser($userId);
// Do the tokens match? Use a timing safe string comparison to prevent timing attacks.
$hashesMatch = Crypt::timingSafeCompare($referenceHMAC, $tokenHMAC);
// Is the user in the allowed user groups?
$inAllowedUserGroups = $this->isInAllowedUserGroup($userId);
/**
* Can we log in?
*
* DO NOT concatenate in a single line. Due to boolean short-circuit evaluation it might
* make timing attacks possible. Using separate lines of code with the previously calculated
* boolean value to the right hand side forces PHP to evaluate the conditions in
* approximately constant time.
*/
// We need non-empty reference token data (the user must have configured a token)
$canLogin = !empty($referenceTokenData);
// The token must be enabled
$canLogin = $enabled && $canLogin;
// The token hash must be calculated with an allowed algorithm
$canLogin = $allowedAlgo && $canLogin;
// The token HMAC hash coming into the request and our reference must match.
$canLogin = $hashesMatch && $canLogin;
// The user must belong in the allowed user groups
$canLogin = $inAllowedUserGroups && $canLogin;
/**
* DO NOT try to be smart and do an early return when either of the individual conditions
* are not met. There's a reason we only return after checking all three conditions: it
* prevents timing attacks.
*/
if (!$canLogin) {
return;
}
// Get the actual user record
$user = $this->getUserFactory()->loadUserById($userId);
// Disallow login for blocked, inactive or password reset required users
if ($user->block || !empty(trim($user->activation)) || $user->requireReset) {
$response->status = Authentication::STATUS_DENIED;
return;
}
// Update the response to indicate successful login
$response->status = Authentication::STATUS_SUCCESS;
$response->error_message = '';
$response->username = $user->username;
$response->email = $user->email;
$response->fullname = $user->name;
$response->timezone = $user->get('timezone');
$response->language = $user->get('language');
// Stop event propagation when status is STATUS_SUCCESS
$event->stopPropagation();
}
/**
* Retrieve the token seed string for the given user ID.
*
* @param int $userId The numeric user ID to return the token seed string for.
*
* @return string|null Null if there is no token configured or the user doesn't exist.
* @since 4.0.0
*/
private function getTokenSeedForUser(int $userId): ?string
{
try {
$db = $this->getDatabase();
$query = $db->getQuery(true)
->select($db->quoteName('profile_value'))
->from($db->quoteName('#__user_profiles'))
->where($db->quoteName('profile_key') . ' = :profileKey')
->where($db->quoteName('user_id') . ' = :userId');
$profileKey = $this->profileKeyPrefix . '.token';
$query->bind(':profileKey', $profileKey, ParameterType::STRING);
$query->bind(':userId', $userId, ParameterType::INTEGER);
return $db->setQuery($query)->loadResult();
} catch (\Exception $e) {
return null;
}
}
/**
* Is the token enabled for a given user ID? If the user does not exist or has no token it
* returns false.
*
* @param int $userId The User ID to check whether the token is enabled on their account.
*
* @return boolean
* @since 4.0.0
*/
private function isTokenEnabledForUser(int $userId): bool
{
try {
$db = $this->getDatabase();
$query = $db->getQuery(true)
->select($db->quoteName('profile_value'))
->from($db->quoteName('#__user_profiles'))
->where($db->quoteName('profile_key') . ' = :profileKey')
->where($db->quoteName('user_id') . ' = :userId');
$profileKey = $this->profileKeyPrefix . '.enabled';
$query->bind(':profileKey', $profileKey, ParameterType::STRING);
$query->bind(':userId', $userId, ParameterType::INTEGER);
$value = $db->setQuery($query)->loadResult();
return $value == 1;
} catch (\Exception $e) {
return false;
}
}
/**
* Retrieves a configuration parameter of a different plugin than the current one.
*
* @param string $folder Plugin folder
* @param string $plugin Plugin name
* @param string $param Parameter name
* @param null $default Default value, in case the parameter is missing
*
* @return mixed
* @since 4.0.0
*/
private function getPluginParameter(string $folder, string $plugin, string $param, $default = null)
{
/** @var PluginModel $model */
$model = $this->getApplication()->bootComponent('plugins')
->getMVCFactory()->createModel('Plugin', 'Administrator', ['ignore_request' => true]);
$pluginObject = $model->getItem(['folder' => $folder, 'element' => $plugin]);
if (!\is_object($pluginObject) || !$pluginObject->enabled || !\array_key_exists($param, $pluginObject->params)) {
return $default;
}
return $pluginObject->params[$param];
}
/**
* Get the configured user groups which are allowed to have access to tokens.
*
* @return int[]
* @since 4.0.0
*/
private function getAllowedUserGroups(): array
{
$userGroups = $this->getPluginParameter('user', 'token', 'allowedUserGroups', [8]);
if (empty($userGroups)) {
return [];
}
if (!\is_array($userGroups)) {
$userGroups = [$userGroups];
}
return $userGroups;
}
/**
* Is the user with the given ID in the allowed User Groups with access to tokens?
*
* @param int $userId The user ID to check
*
* @return boolean False when doesn't belong to allowed user groups, user not found, or guest
* @since 4.0.0
*/
private function isInAllowedUserGroup($userId)
{
$allowedUserGroups = $this->getAllowedUserGroups();
$user = $this->getUserFactory()->loadUserById($userId);
if ($user->id != $userId) {
return false;
}
if ($user->guest) {
return false;
}
// No specifically allowed user groups: allow ALL user groups.
if (empty($allowedUserGroups)) {
return true;
}
$groups = $user->getAuthorisedGroups();
$intersection = array_intersect($groups, $allowedUserGroups);
return !empty($intersection);
}
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="api-authentication" method="upgrade">
<name>plg_api-authentication_token</name>
<author>Joomla! Project</author>
<creationDate>2019-11</creationDate>
<copyright>(C) 2020 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>4.0.0</version>
<description>PLG_API-AUTHENTICATION_TOKEN_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\ApiAuthentication\Token</namespace>
<files>
<folder plugin="token">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_api-authentication_token.ini</language>
<language tag="en-GB">language/en-GB/plg_api-authentication_token.sys.ini</language>
</languages>
</extension>

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="authentication" method="upgrade">
<name>plg_authentication_cookie</name>
<author>Joomla! Project</author>
<creationDate>2013-07</creationDate>
<copyright>(C) 2013 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>3.0.0</version>
<description>PLG_AUTHENTICATION_COOKIE_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\Authentication\Cookie</namespace>
<files>
<folder plugin="cookie">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_authentication_cookie.ini</language>
<language tag="en-GB">language/en-GB/plg_authentication_cookie.sys.ini</language>
</languages>
<config>
<fields name="params">
<fieldset name="basic">
<field
name="cookie_lifetime"
type="number"
label="PLG_AUTHENTICATION_COOKIE_FIELD_COOKIE_LIFETIME_LABEL"
default="60"
filter="integer"
required="true"
/>
<field
name="key_length"
type="list"
label="PLG_AUTHENTICATION_COOKIE_FIELD_KEY_LENGTH_LABEL"
default="16"
filter="integer"
required="true"
validate="options"
>
<option value="8">8</option>
<option value="16">16</option>
<option value="32">32</option>
<option value="64">64</option>
</field>
</fieldset>
</fields>
</config>
</extension>

View File

@ -0,0 +1,50 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Authentication.cookie
*
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\User\UserFactoryInterface;
use Joomla\Database\DatabaseInterface;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Authentication\Cookie\Extension\Cookie;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.3.0
*/
public function register(Container $container)
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new Cookie(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('authentication', 'cookie')
);
$plugin->setApplication(Factory::getApplication());
$plugin->setDatabase($container->get(DatabaseInterface::class));
$plugin->setUserFactory($container->get(UserFactoryInterface::class));
return $plugin;
}
);
}
};

View File

@ -0,0 +1,404 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Authentication.cookie
*
* @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\Plugin\Authentication\Cookie\Extension;
use Joomla\CMS\Authentication\Authentication;
use Joomla\CMS\Filter\InputFilter;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Log\Log;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\User\UserFactoryAwareTrait;
use Joomla\CMS\User\UserHelper;
use Joomla\Database\DatabaseAwareTrait;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Joomla Authentication plugin
*
* @since 3.2
* @note Code based on http://jaspan.com/improved_persistent_login_cookie_best_practice
* and http://fishbowl.pastiche.org/2004/01/19/persistent_login_cookie_best_practice/
*/
final class Cookie extends CMSPlugin
{
use DatabaseAwareTrait;
use UserFactoryAwareTrait;
/**
* Reports the privacy related capabilities for this plugin to site administrators.
*
* @return array
*
* @since 3.9.0
*/
public function onPrivacyCollectAdminCapabilities()
{
$this->loadLanguage();
return [
$this->getApplication()->getLanguage()->_('PLG_AUTHENTICATION_COOKIE') => [
$this->getApplication()->getLanguage()->_('PLG_AUTHENTICATION_COOKIE_PRIVACY_CAPABILITY_COOKIE'),
],
];
}
/**
* This method should handle any authentication and report back to the subject
*
* @param array $credentials Array holding the user credentials
* @param array $options Array of extra options
* @param object &$response Authentication response object
*
* @return boolean
*
* @since 3.2
*/
public function onUserAuthenticate($credentials, $options, &$response)
{
$app = $this->getApplication();
// No remember me for admin
if ($app->isClient('administrator')) {
return false;
}
// Get cookie
$cookieName = 'joomla_remember_me_' . UserHelper::getShortHashedUserAgent();
$cookieValue = $app->getInput()->cookie->get($cookieName);
// Try with old cookieName (pre 3.6.0) if not found
if (!$cookieValue) {
$cookieName = UserHelper::getShortHashedUserAgent();
$cookieValue = $app->getInput()->cookie->get($cookieName);
}
if (!$cookieValue) {
return false;
}
$cookieArray = explode('.', $cookieValue);
// Check for valid cookie value
if (\count($cookieArray) !== 2) {
// Destroy the cookie in the browser.
$app->getInput()->cookie->set($cookieName, '', 1, $app->get('cookie_path', '/'), $app->get('cookie_domain', ''));
Log::add('Invalid cookie detected.', Log::WARNING, 'error');
return false;
}
$response->type = 'Cookie';
// Filter series since we're going to use it in the query
$filter = new InputFilter();
$series = $filter->clean($cookieArray[1], 'ALNUM');
$now = time();
// Remove expired tokens
$db = $this->getDatabase();
$query = $db->getQuery(true)
->delete($db->quoteName('#__user_keys'))
->where($db->quoteName('time') . ' < :now')
->bind(':now', $now);
try {
$db->setQuery($query)->execute();
} catch (\RuntimeException $e) {
// We aren't concerned with errors from this query, carry on
}
// Find the matching record if it exists.
$query = $db->getQuery(true)
->select($db->quoteName(['user_id', 'token', 'series', 'time']))
->from($db->quoteName('#__user_keys'))
->where($db->quoteName('series') . ' = :series')
->where($db->quoteName('uastring') . ' = :uastring')
->order($db->quoteName('time') . ' DESC')
->bind(':series', $series)
->bind(':uastring', $cookieName);
try {
$results = $db->setQuery($query)->loadObjectList();
} catch (\RuntimeException $e) {
$response->status = Authentication::STATUS_FAILURE;
return false;
}
if (\count($results) !== 1) {
// Destroy the cookie in the browser.
$app->getInput()->cookie->set($cookieName, '', 1, $app->get('cookie_path', '/'), $app->get('cookie_domain', ''));
$response->status = Authentication::STATUS_FAILURE;
return false;
}
// We have a user with one cookie with a valid series and a corresponding record in the database.
if (!UserHelper::verifyPassword($cookieArray[0], $results[0]->token)) {
/*
* This is a real attack!
* Either the series was guessed correctly or a cookie was stolen and used twice (once by attacker and once by victim).
* Delete all tokens for this user!
*/
$query = $db->getQuery(true)
->delete($db->quoteName('#__user_keys'))
->where($db->quoteName('user_id') . ' = :userid')
->bind(':userid', $results[0]->user_id);
try {
$db->setQuery($query)->execute();
} catch (\RuntimeException $e) {
// Log an alert for the site admin
Log::add(
sprintf('Failed to delete cookie token for user %s with the following error: %s', $results[0]->user_id, $e->getMessage()),
Log::WARNING,
'security'
);
}
// Destroy the cookie in the browser.
$app->getInput()->cookie->set($cookieName, '', 1, $app->get('cookie_path', '/'), $app->get('cookie_domain', ''));
// Issue warning by email to user and/or admin?
Log::add(Text::sprintf('PLG_AUTHENTICATION_COOKIE_ERROR_LOG_LOGIN_FAILED', $results[0]->user_id), Log::WARNING, 'security');
$response->status = Authentication::STATUS_FAILURE;
return false;
}
// Make sure there really is a user with this name and get the data for the session.
$query = $db->getQuery(true)
->select($db->quoteName(['id', 'username', 'password']))
->from($db->quoteName('#__users'))
->where($db->quoteName('username') . ' = :userid')
->where($db->quoteName('requireReset') . ' = 0')
->bind(':userid', $results[0]->user_id);
try {
$result = $db->setQuery($query)->loadObject();
} catch (\RuntimeException $e) {
$response->status = Authentication::STATUS_FAILURE;
return false;
}
if ($result) {
// Bring this in line with the rest of the system
$user = $this->getUserFactory()->loadUserById($result->id);
// Set response data.
$response->username = $result->username;
$response->email = $user->email;
$response->fullname = $user->name;
$response->password = $result->password;
$response->language = $user->getParam('language');
// Set response status.
$response->status = Authentication::STATUS_SUCCESS;
$response->error_message = '';
} else {
$response->status = Authentication::STATUS_FAILURE;
$response->error_message = $app->getLanguage()->_('JGLOBAL_AUTH_NO_USER');
}
}
/**
* We set the authentication cookie only after login is successfully finished.
* We set a new cookie either for a user with no cookies or one
* where the user used a cookie to authenticate.
*
* @param array $options Array holding options
*
* @return boolean True on success
*
* @since 3.2
*/
public function onUserAfterLogin($options)
{
$app = $this->getApplication();
// No remember me for admin
if ($app->isClient('administrator')) {
return false;
}
$db = $this->getDatabase();
if (isset($options['responseType']) && $options['responseType'] === 'Cookie') {
// Logged in using a cookie
$cookieName = 'joomla_remember_me_' . UserHelper::getShortHashedUserAgent();
// We need the old data to get the existing series
$cookieValue = $app->getInput()->cookie->get($cookieName);
// Try with old cookieName (pre 3.6.0) if not found
if (!$cookieValue) {
$oldCookieName = UserHelper::getShortHashedUserAgent();
$cookieValue = $app->getInput()->cookie->get($oldCookieName);
// Destroy the old cookie in the browser
$app->getInput()->cookie->set($oldCookieName, '', 1, $app->get('cookie_path', '/'), $app->get('cookie_domain', ''));
}
$cookieArray = explode('.', $cookieValue);
// Filter series since we're going to use it in the query
$filter = new InputFilter();
$series = $filter->clean($cookieArray[1], 'ALNUM');
} elseif (!empty($options['remember'])) {
// Remember checkbox is set
$cookieName = 'joomla_remember_me_' . UserHelper::getShortHashedUserAgent();
// Create a unique series which will be used over the lifespan of the cookie
$unique = false;
$errorCount = 0;
do {
$series = UserHelper::genRandomPassword(20);
$query = $db->getQuery(true)
->select($db->quoteName('series'))
->from($db->quoteName('#__user_keys'))
->where($db->quoteName('series') . ' = :series')
->bind(':series', $series);
try {
$results = $db->setQuery($query)->loadResult();
if ($results === null) {
$unique = true;
}
} catch (\RuntimeException $e) {
$errorCount++;
// We'll let this query fail up to 5 times before giving up, there's probably a bigger issue at this point
if ($errorCount === 5) {
return false;
}
}
} while ($unique === false);
} else {
return false;
}
// Get the parameter values
$lifetime = $this->params->get('cookie_lifetime', 60) * 24 * 60 * 60;
$length = $this->params->get('key_length', 16);
// Generate new cookie
$token = UserHelper::genRandomPassword($length);
$cookieValue = $token . '.' . $series;
// Overwrite existing cookie with new value
$app->getInput()->cookie->set(
$cookieName,
$cookieValue,
time() + $lifetime,
$app->get('cookie_path', '/'),
$app->get('cookie_domain', ''),
$app->isHttpsForced(),
true
);
$query = $db->getQuery(true);
if (!empty($options['remember'])) {
$future = (time() + $lifetime);
// Create new record
$query
->insert($db->quoteName('#__user_keys'))
->set($db->quoteName('user_id') . ' = :userid')
->set($db->quoteName('series') . ' = :series')
->set($db->quoteName('uastring') . ' = :uastring')
->set($db->quoteName('time') . ' = :time')
->bind(':userid', $options['user']->username)
->bind(':series', $series)
->bind(':uastring', $cookieName)
->bind(':time', $future);
} else {
// Update existing record with new token
$query
->update($db->quoteName('#__user_keys'))
->where($db->quoteName('user_id') . ' = :userid')
->where($db->quoteName('series') . ' = :series')
->where($db->quoteName('uastring') . ' = :uastring')
->bind(':userid', $options['user']->username)
->bind(':series', $series)
->bind(':uastring', $cookieName);
}
$hashedToken = UserHelper::hashPassword($token);
$query->set($db->quoteName('token') . ' = :token')
->bind(':token', $hashedToken);
try {
$db->setQuery($query)->execute();
} catch (\RuntimeException $e) {
return false;
}
return true;
}
/**
* This is where we delete any authentication cookie when a user logs out
*
* @param array $options Array holding options (length, timeToExpiration)
*
* @return boolean True on success
*
* @since 3.2
*/
public function onUserAfterLogout($options)
{
$app = $this->getApplication();
// No remember me for admin
if ($app->isClient('administrator')) {
return false;
}
$cookieName = 'joomla_remember_me_' . UserHelper::getShortHashedUserAgent();
$cookieValue = $app->getInput()->cookie->get($cookieName);
// There are no cookies to delete.
if (!$cookieValue) {
return true;
}
$cookieArray = explode('.', $cookieValue);
// Filter series since we're going to use it in the query
$filter = new InputFilter();
$series = $filter->clean($cookieArray[1], 'ALNUM');
// Remove the record from the database
$db = $this->getDatabase();
$query = $db->getQuery(true)
->delete($db->quoteName('#__user_keys'))
->where($db->quoteName('series') . ' = :series')
->bind(':series', $series);
try {
$db->setQuery($query)->execute();
} catch (\RuntimeException $e) {
// We aren't concerned with errors from this query, carry on
}
// Destroy the cookie
$app->getInput()->cookie->set($cookieName, '', 1, $app->get('cookie_path', '/'), $app->get('cookie_domain', ''));
return true;
}
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="authentication" method="upgrade">
<name>plg_authentication_joomla</name>
<author>Joomla! Project</author>
<creationDate>2005-11</creationDate>
<copyright>(C) 2005 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>3.0.0</version>
<description>PLG_AUTHENTICATION_JOOMLA_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\Authentication\Joomla</namespace>
<files>
<folder plugin="joomla">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_authentication_joomla.ini</language>
<language tag="en-GB">language/en-GB/plg_authentication_joomla.sys.ini</language>
</languages>
</extension>

View File

@ -0,0 +1,50 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Authentication.joomla
*
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\User\UserFactoryInterface;
use Joomla\Database\DatabaseInterface;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Authentication\Joomla\Extension\Joomla;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.3.0
*/
public function register(Container $container)
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new Joomla(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('authentication', 'joomla')
);
$plugin->setApplication(Factory::getApplication());
$plugin->setDatabase($container->get(DatabaseInterface::class));
$plugin->setUserFactory($container->get(UserFactoryInterface::class));
return $plugin;
}
);
}
};

View File

@ -0,0 +1,128 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Authentication.joomla
*
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Plugin\Authentication\Joomla\Extension;
use Joomla\CMS\Authentication\Authentication;
use Joomla\CMS\Event\User\AuthenticationEvent;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\User\UserFactoryAwareTrait;
use Joomla\CMS\User\UserHelper;
use Joomla\Database\DatabaseAwareTrait;
use Joomla\Event\SubscriberInterface;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Joomla Authentication plugin
*
* @since 1.5
*/
final class Joomla extends CMSPlugin implements SubscriberInterface
{
use DatabaseAwareTrait;
use UserFactoryAwareTrait;
/**
* Returns an array of events this subscriber will listen to.
*
* @return array
*
* @since 5.0.0
*/
public static function getSubscribedEvents(): array
{
return ['onUserAuthenticate' => 'onUserAuthenticate'];
}
/**
* This method should handle any authentication and report back to the subject
*
* @param AuthenticationEvent $event Authentication event
*
* @return void
*
* @since 1.5
*/
public function onUserAuthenticate(AuthenticationEvent $event)
{
$credentials = $event->getCredentials();
$response = $event->getAuthenticationResponse();
$response->type = 'Joomla';
// Joomla does not like blank passwords
if (empty($credentials['password'])) {
$response->status = Authentication::STATUS_FAILURE;
$response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_EMPTY_PASS_NOT_ALLOWED');
return;
}
$db = $this->getDatabase();
$query = $db->getQuery(true)
->select($db->quoteName(['id', 'password']))
->from($db->quoteName('#__users'))
->where($db->quoteName('username') . ' = :username')
->bind(':username', $credentials['username']);
$db->setQuery($query);
$result = $db->loadObject();
if ($result) {
$match = UserHelper::verifyPassword($credentials['password'], $result->password, $result->id);
if ($match === true) {
// Bring this in line with the rest of the system
$user = $this->getUserFactory()->loadUserById($result->id);
$response->email = $user->email;
$response->fullname = $user->name;
// Set default status response to success
$_status = Authentication::STATUS_SUCCESS;
$_errorMessage = '';
if ($this->getApplication()->isClient('administrator')) {
$response->language = $user->getParam('admin_language');
} else {
$response->language = $user->getParam('language');
if ($this->getApplication()->get('offline') && !$user->authorise('core.login.offline')) {
// User do not have access in offline mode
$_status = Authentication::STATUS_FAILURE;
$_errorMessage = $this->getApplication()->getLanguage()->_('JLIB_LOGIN_DENIED');
}
}
$response->status = $_status;
$response->error_message = $_errorMessage;
// Stop event propagation when status is STATUS_SUCCESS
if ($response->status === Authentication::STATUS_SUCCESS) {
$event->stopPropagation();
}
} else {
// Invalid password
$response->status = Authentication::STATUS_FAILURE;
$response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_INVALID_PASS');
}
} else {
// Let's hash the entered password even if we don't have a matching user for some extra response time
// By doing so, we mitigate side channel user enumeration attacks
UserHelper::hashPassword($credentials['password']);
// Invalid user
$response->status = Authentication::STATUS_FAILURE;
$response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_NO_USER');
}
}
}

View File

@ -0,0 +1,186 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="authentication" method="upgrade">
<name>plg_authentication_ldap</name>
<author>Joomla! Project</author>
<creationDate>2005-11</creationDate>
<copyright>(C) 2005 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>3.0.0</version>
<description>PLG_LDAP_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\Authentication\Ldap</namespace>
<files>
<folder plugin="ldap">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_authentication_ldap.ini</language>
<language tag="en-GB">language/en-GB/plg_authentication_ldap.sys.ini</language>
</languages>
<config>
<fields name="params">
<fieldset name="basic">
<field
name="host"
type="text"
label="PLG_LDAP_FIELD_HOST_LABEL"
required="true"
/>
<field
name="port"
type="number"
label="PLG_LDAP_FIELD_PORT_LABEL"
min="1"
max="65535"
default="389"
hint="389"
validate="number"
filter="integer"
/>
<field
name="use_ldapV3"
type="radio"
layout="joomla.form.field.radio.switcher"
label="PLG_LDAP_FIELD_V3_LABEL"
default="1"
filter="integer"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
<field
name="encryption"
type="list"
label="PLG_LDAP_FIELD_ENCRYPTION_LABEL"
default="none"
validate="options"
>
<option value="none">PLG_LDAP_FIELD_VALUE_ENCRYPTIONNONE</option>
<option value="tls">PLG_LDAP_FIELD_VALUE_ENCRYPTIONTLS</option>
<option value="ssl">PLG_LDAP_FIELD_VALUE_ENCRYPTIONSSL</option>
</field>
<field
name="ignore_reqcert_tls"
type="radio"
label="PLG_LDAP_FIELD_IGNORE_REQCERT_TLS_LABEL"
layout="joomla.form.field.radio.switcher"
default="1"
filter="boolean"
showon="encryption!:none"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
<field
name="cacert"
type="text"
label="PLG_LDAP_FIELD_CACERT_LABEL"
description="PLG_LDAP_FIELD_CACERT_DESC"
required="false"
showon="encryption!:none[AND]ignore_reqcert_tls:0"
/>
<field
name="no_referrals"
type="radio"
label="PLG_LDAP_FIELD_REFERRALS_LABEL"
default="0"
filter="integer"
layout="joomla.form.field.radio.switcher"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
<field
name="auth_method"
type="list"
label="PLG_LDAP_FIELD_AUTHMETHOD_LABEL"
default="bind"
validate="options"
>
<option value="search">PLG_LDAP_FIELD_VALUE_BINDSEARCH</option>
<option value="bind">PLG_LDAP_FIELD_VALUE_BINDUSER</option>
</field>
<field
name="base_dn"
type="text"
label="PLG_LDAP_FIELD_BASEDN_LABEL"
required="true"
/>
<field
name="search_string"
type="text"
label="PLG_LDAP_FIELD_SEARCHSTRING_LABEL"
description="PLG_LDAP_FIELD_SEARCHSTRING_DESC"
required="true"
/>
<field
name="users_dn"
type="text"
label="PLG_LDAP_FIELD_USERSDN_LABEL"
description="PLG_LDAP_FIELD_USERSDN_DESC"
/>
<field
name="username"
type="text"
label="PLG_LDAP_FIELD_USERNAME_LABEL"
description="PLG_LDAP_FIELD_USERNAME_DESC"
/>
<field
name="password"
type="password"
label="PLG_LDAP_FIELD_PASSWORD_LABEL"
description="PLG_LDAP_FIELD_PASSWORD_DESC"
/>
<field
name="ldap_fullname"
type="text"
label="PLG_LDAP_FIELD_FULLNAME_LABEL"
description="PLG_LDAP_FIELD_FULLNAME_DESC"
default="fullName"
/>
<field
name="ldap_email"
type="text"
label="PLG_LDAP_FIELD_EMAIL_LABEL"
description="PLG_LDAP_FIELD_EMAIL_DESC"
default="mail"
/>
<field
name="ldap_uid"
type="text"
label="PLG_LDAP_FIELD_UID_LABEL"
description="PLG_LDAP_FIELD_UID_DESC"
default="uid"
/>
<field
name="ldap_debug"
type="radio"
label="PLG_LDAP_FIELD_LDAPDEBUG_LABEL"
description="PLG_LDAP_FIELD_LDAPDEBUG_DESC"
default="0"
filter="integer"
layout="joomla.form.field.radio.switcher"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
</fieldset>
</fields>
</config>
</extension>

View File

@ -0,0 +1,48 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Authentication.ldap
*
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Authentication\Ldap\Extension\Ldap;
use Joomla\Plugin\Authentication\Ldap\Factory\LdapFactory;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.3.0
*/
public function register(Container $container)
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new Ldap(
new LdapFactory(),
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('authentication', 'ldap')
);
$plugin->setApplication(Factory::getApplication());
return $plugin;
}
);
}
};

View File

@ -0,0 +1,298 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Authentication.ldap
*
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Plugin\Authentication\Ldap\Extension;
use Joomla\CMS\Authentication\Authentication;
use Joomla\CMS\Log\Log;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Authentication\Ldap\Factory\LdapFactoryInterface;
use Symfony\Component\Ldap\Entry;
use Symfony\Component\Ldap\Exception\ConnectionException;
use Symfony\Component\Ldap\Exception\LdapException;
use Symfony\Component\Ldap\LdapInterface;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* LDAP Authentication Plugin
*
* @since 1.5
*/
final class Ldap extends CMSPlugin
{
/**
* The ldap factory
*
* @var LdapFactoryInterface
* @since 4.3.0
*/
private $factory;
/**
* Constructor
*
* @param LdapFactoryInterface $factory The Ldap factory
* @param DispatcherInterface $dispatcher The object to observe
* @param array $config An optional associative array of configuration settings.
* Recognized key values include 'name', 'group', 'params', 'language'
* (this list is not meant to be comprehensive).
*
* @since 4.3.0
*/
public function __construct(LdapFactoryInterface $factory, DispatcherInterface $dispatcher, array $config = [])
{
parent::__construct($dispatcher, $config);
$this->factory = $factory;
}
/**
* This method should handle any authentication and report back to the subject
*
* @param array $credentials Array holding the user credentials
* @param array $options Array of extra options
* @param object &$response Authentication response object
*
* @return boolean
*
* @since 1.5
*/
public function onUserAuthenticate($credentials, $options, &$response)
{
// If LDAP not correctly configured then bail early.
if (!$this->params->get('host', '')) {
return false;
}
// For JLog
$logcategory = 'ldap';
$response->type = $logcategory;
// Strip null bytes from the password
$credentials['password'] = str_replace(\chr(0), '', $credentials['password']);
// LDAP does not like Blank passwords (tries to Anon Bind which is bad)
if (empty($credentials['password'])) {
$response->status = Authentication::STATUS_FAILURE;
$response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_EMPTY_PASS_NOT_ALLOWED');
return false;
}
// Load plugin params info
$ldap_email = $this->params->get('ldap_email', '');
$ldap_fullname = $this->params->get('ldap_fullname', '');
$ldap_uid = $this->params->get('ldap_uid', '');
$auth_method = $this->params->get('auth_method', '');
// Load certificate info
$ignore_reqcert_tls = (bool) $this->params->get('ignore_reqcert_tls', '1');
$cacert = $this->params->get('cacert', '');
// getting certificate file and certificate directory options (both need to be set)
if (!$ignore_reqcert_tls && !empty($cacert)) {
if (is_dir($cacert)) {
$cacertdir = $cacert;
$cacertfile = "";
} elseif (is_file($cacert)) {
$cacertfile = $cacert;
$cacertdir = \dirname($cacert);
} else {
$cacertfile = $cacert;
$cacertdir = $cacert;
Log::add(sprintf('Certificate path for LDAP client is neither an existing file nor directory: "%s"', $cacert), Log::ERROR, $logcategory);
}
} else {
Log::add(sprintf('Not setting any LDAP TLS CA certificate options because %s, system wide settings are used', $ignore_reqcert_tls ? "certificate is ignored" : "no certificate location is configured"), Log::DEBUG, $logcategory);
}
$options = [
'host' => $this->params->get('host', ''),
'port' => (int) $this->params->get('port', ''),
'version' => $this->params->get('use_ldapV3', '1') == '1' ? 3 : 2,
'referrals' => (bool) $this->params->get('no_referrals', '0'),
'encryption' => $this->params->get('encryption', 'none'),
'debug' => (bool) $this->params->get('ldap_debug', '0'),
'options' => [
'x_tls_require_cert' => $ignore_reqcert_tls ? LDAP_OPT_X_TLS_NEVER : LDAP_OPT_X_TLS_DEMAND,
],
];
// if these are not set, the system defaults are used
if (isset($cacertdir) && isset($cacertfile)) {
$options['options']['x_tls_cacertdir'] = $cacertdir;
$options['options']['x_tls_cacertfile'] = $cacertfile;
}
Log::add(sprintf('Creating LDAP session with options: %s', json_encode($options)), Log::DEBUG, $logcategory);
$connection_string = sprintf('ldap%s://%s:%s', 'ssl' === $options['encryption'] ? 's' : '', $options['host'], $options['port']);
Log::add(sprintf('Creating LDAP session to connect to "%s" while binding', $connection_string), Log::DEBUG, $logcategory);
$ldap = $this->factory->createLdap($options);
switch ($auth_method) {
case 'search':
try {
$dn = $this->params->get('username', '');
Log::add(sprintf('Binding to LDAP server with administrative dn "%s" and given administrative password (anonymous if user dn is blank)', $dn), Log::DEBUG, $logcategory);
$ldap->bind($dn, $this->params->get('password', ''));
} catch (ConnectionException | LdapException $exception) {
$response->status = Authentication::STATUS_FAILURE;
$response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_NOT_CONNECT');
Log::add($exception->getMessage(), Log::ERROR, $logcategory);
return;
}
// Search for users DN
try {
$searchstring = str_replace(
'[search]',
str_replace(';', '\3b', $ldap->escape($credentials['username'], '', LDAP_ESCAPE_FILTER)),
$this->params->get('search_string', '')
);
Log::add(sprintf('Searching LDAP entry with filter: "%s"', $searchstring), Log::DEBUG, $logcategory);
$entry = $this->searchByString($searchstring, $ldap);
} catch (LdapException $exception) {
$response->status = Authentication::STATUS_FAILURE;
$response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_UNKNOWN_ACCESS_DENIED');
Log::add($exception->getMessage(), Log::ERROR, $logcategory);
return;
}
if (!$entry) {
// we did not find the login in LDAP
$response->status = Authentication::STATUS_FAILURE;
$response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_NO_USER');
Log::add($this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_USER_NOT_FOUND'), Log::ERROR, $logcategory);
return;
}
Log::add(sprintf('LDAP entry found at "%s"', $entry->getDn()), Log::DEBUG, $logcategory);
try {
// Verify Users Credentials
Log::add(sprintf('Binding to LDAP server with found user dn "%s" and user entered password', $entry->getDn()), Log::DEBUG, $logcategory);
$ldap->bind($entry->getDn(), $credentials['password']);
} catch (ConnectionException $exception) {
$response->status = Authentication::STATUS_FAILURE;
$response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_INVALID_PASS');
Log::add($exception->getMessage(), Log::ERROR, $logcategory);
return;
}
break;
case 'bind':
// We just accept the result here
try {
if ($this->params->get('users_dn', '') == '') {
$dn = $credentials['username'];
} else {
$dn = str_replace(
'[username]',
$ldap->escape($credentials['username'], '', LDAP_ESCAPE_DN),
$this->params->get('users_dn', '')
);
}
Log::add(sprintf('Direct binding to LDAP server with entered user dn "%s" and user entered password', $dn), Log::DEBUG, $logcategory);
$ldap->bind($dn, $credentials['password']);
} catch (ConnectionException | LdapException $exception) {
$response->status = Authentication::STATUS_FAILURE;
$response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_INVALID_PASS');
Log::add($exception->getMessage(), Log::ERROR, $logcategory);
return;
}
try {
$searchstring = str_replace(
'[search]',
str_replace(';', '\3b', $ldap->escape($credentials['username'], '', LDAP_ESCAPE_FILTER)),
$this->params->get('search_string', '')
);
Log::add(sprintf('Searching LDAP entry with filter: "%s"', $searchstring), Log::DEBUG, $logcategory);
$entry = $this->searchByString($searchstring, $ldap);
} catch (LdapException $exception) {
$response->status = Authentication::STATUS_FAILURE;
$response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_UNKNOWN_ACCESS_DENIED');
Log::add($exception->getMessage(), Log::ERROR, $logcategory);
return;
}
if (!$entry) {
// we did not find the login in LDAP
$response->status = Authentication::STATUS_FAILURE;
$response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_NO_USER');
Log::add($this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_USER_NOT_FOUND'), Log::ERROR, $logcategory);
return;
}
Log::add(sprintf('LDAP entry found at "%s"', $entry->getDn()), Log::DEBUG, $logcategory);
break;
default:
// Unsupported configuration
$response->status = Authentication::STATUS_FAILURE;
$response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_UNKNOWN_ACCESS_DENIED');
Log::add($response->error_message, Log::ERROR, $logcategory);
return;
}
// Grab some details from LDAP and return them
$response->username = $entry->getAttribute($ldap_uid)[0] ?? false;
$response->email = $entry->getAttribute($ldap_email)[0] ?? false;
$response->fullname = $entry->getAttribute($ldap_fullname)[0] ?? $credentials['username'];
// Were good - So say so.
Log::add(sprintf('LDAP login succeeded; username: "%s", email: "%s", fullname: "%s"', $response->username, $response->email, $response->fullname), Log::DEBUG, $logcategory);
$response->status = Authentication::STATUS_SUCCESS;
$response->error_message = '';
// The connection is no longer needed, destroy the object to close it
unset($ldap);
}
/**
* Shortcut method to perform a LDAP search based on a semicolon separated string
*
* Note that this method requires that semicolons which should be part of the search term to be escaped
* to correctly split the search string into separate lookups
*
* @param string $search search string of search values
* @param LdapInterface $ldap The LDAP client
*
* @return Entry|null The search result entry if a matching record was found
*
* @since 3.8.2
*/
private function searchByString(string $search, LdapInterface $ldap)
{
$dn = $this->params->get('base_dn', '');
// We return the first entry from the first search result which contains data
foreach (explode(';', $search) as $key => $result) {
$results = $ldap->query($dn, '(' . str_replace('\3b', ';', $result) . ')')->execute();
if (\count($results)) {
return $results[0];
}
}
}
}

View File

@ -0,0 +1,41 @@
<?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\Plugin\Authentication\Ldap\Factory;
use Symfony\Component\Ldap\Ldap;
use Symfony\Component\Ldap\LdapInterface;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Factory to create Ldap clients.
*
* @since 4.3.0
*/
class LdapFactory implements LdapFactoryInterface
{
/**
* Method to load and return an Ldap client.
*
* @param array $config The configuration array for the ldap client
*
* @return LdapInterface
*
* @since 4.3.0
*
* @throws \Exception
*/
public function createLdap(array $config): LdapInterface
{
return Ldap::create('ext_ldap', $config);
}
}

View File

@ -0,0 +1,36 @@
<?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\Plugin\Authentication\Ldap\Factory;
use Symfony\Component\Ldap\LdapInterface;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Factory to create Ldap clients.
*
* @since 4.3.0
*/
interface LdapFactoryInterface
{
/**
* Method to load and return an Ldap client.
*
* @param array $config The configuration array for the ldap client
*
* @return LdapInterface
*
* @since 4.3.0
* @throws \Exception
*/
public function createLdap(array $config): LdapInterface;
}

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="behaviour" method="upgrade">
<name>plg_behaviour_compat</name>
<author>Joomla! Project</author>
<creationDate>2023-09</creationDate>
<copyright>(C) 2023 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>5.0.0</version>
<description>PLG_COMPAT_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\Behaviour\Compat</namespace>
<files>
<folder plugin="compat">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_behaviour_compat.ini</language>
<language tag="en-GB">language/en-GB/plg_behaviour_compat.sys.ini</language>
</languages>
<config>
<fields name="params">
<fieldset name="basic">
<field
name="classes_aliases"
type="radio"
layout="joomla.form.field.radio.switcher"
label="PLG_COMPAT_FIELD_CLASSES_ALIASES_LABEL"
description="PLG_COMPAT_FIELD_CLASSES_ALIASES_DESCRIPTION"
default="1"
filter="integer"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
<field
name="es5_assets"
type="radio"
label="PLG_COMPAT_FIELD_ES5_ASSETS_LABEL"
description="PLG_COMPAT_FIELD_ES5_ASSETS_DESCRIPTION"
layout="joomla.form.field.radio.switcher"
default="1"
filter="integer"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
<field
name="removed_asset"
type="radio"
label="PLG_COMPAT_FIELD_REMOVED_ASSETS_LABEL"
description="PLG_COMPAT_FIELD_REMOVED_ASSETS_DESCRIPTION"
layout="joomla.form.field.radio.switcher"
default="1"
filter="integer"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
</fieldset>
</fields>
</config>
</extension>

View File

@ -0,0 +1,45 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Behaviour.compat
*
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Behaviour\Compat\Extension\Compat;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
* @since 4.4.0
*/
public function register(Container $container)
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = PluginHelper::getPlugin('behaviour', 'compat');
$dispatcher = $container->get(DispatcherInterface::class);
$plugin = new Compat($dispatcher, (array) $plugin);
$plugin->setApplication(Factory::getApplication());
return $plugin;
}
);
}
};

View File

@ -0,0 +1,113 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Behaviour.compat
*
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Plugin\Behaviour\Compat\Extension;
use Joomla\CMS\Event\Application\AfterInitialiseDocumentEvent;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\DispatcherInterface;
use Joomla\Event\Event;
use Joomla\Event\Priority;
use Joomla\Event\SubscriberInterface;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Joomla! Compat Plugin.
*
* @since 4.4.0
*/
final class Compat extends CMSPlugin implements SubscriberInterface
{
/**
* Returns an array of CMS events this plugin will listen to and the respective handlers.
*
* @return array
*
* @since 4.4.0
*/
public static function getSubscribedEvents(): array
{
/**
* Note that onAfterInitialise must be the first handlers to run for this
* plugin to operate as expected. These handlers load compatibility code which
* might be needed by other plugins
*/
return [
'onAfterInitialiseDocument' => ['onAfterInitialiseDocument', Priority::HIGH],
];
}
/**
* Constructor
*
* @param DispatcherInterface $dispatcher The event dispatcher
* @param array $config An optional associative array of configuration settings.
* Recognized key values include 'name', 'group', 'params', 'language'
* (this list is not meant to be comprehensive).
*
* @since 1.5
*/
public function __construct(DispatcherInterface $dispatcher, array $config = [])
{
parent::__construct($dispatcher, $config);
/**
* Normally we should never use the constructor to execute any logic which would
* affect other parts of the cms, but since we need to load class aliases as
* early as possible we load the class aliases in the constructor so behaviour and system plugins
* which depend on the JPlugin alias for example still are working
*/
/**
* Load class names which are deprecated in joomla 4.0 and which will
* likely be removed in Joomla 6.0
*/
if ($this->params->get('classes_aliases', '1')) {
require_once \dirname(__DIR__) . '/classmap/classmap.php';
}
}
/**
* We run as early as possible, this should be the first event
*
* @param Event $event
* @return void
*
* @since 5.0.0
*/
public function onAfterInitialiseDocument(AfterInitialiseDocumentEvent $event)
{
/**
* Load the es5 assets stubs, they are needed if an extension
* directly uses a core es5 asset which has no function in Joomla 5+
* and only provides an empty asset to not throw an exception
*/
if ($this->params->get('es5_assets', '1')) {
$event->getDocument()
->getWebAssetManager()
->getRegistry()
->addRegistryFile('media/plg_behaviour_compat/es5.asset.json');
}
/**
* Load the removed assets stubs, they are needed if an extension
* directly uses a core asset from Joomla 4 which is not present in Joomla 5+
* and only provides an empty asset to not throw an exception
*/
if ($this->params->get('removed_asset', '1')) {
$event->getDocument()
->getWebAssetManager()
->getRegistry()
->addRegistryFile('media/plg_behaviour_compat/removed.asset.json');
}
}
}

View File

@ -0,0 +1,532 @@
<?php
/**
* Joomla! Content Management System
*
* @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
// No direct access.
\defined('_JEXEC') or die;
require_once __DIR__ . '/extensions.classmap.php';
JLoader::registerAlias('JRegistry', '\\Joomla\\Registry\\Registry', '6.0');
JLoader::registerAlias('JRegistryFormatIni', '\\Joomla\\Registry\\Format\\Ini', '6.0');
JLoader::registerAlias('JRegistryFormatJson', '\\Joomla\\Registry\\Format\\Json', '6.0');
JLoader::registerAlias('JRegistryFormatPhp', '\\Joomla\\Registry\\Format\\Php', '6.0');
JLoader::registerAlias('JRegistryFormatXml', '\\Joomla\\Registry\\Format\\Xml', '6.0');
JLoader::registerAlias('JStringInflector', '\\Joomla\\String\\Inflector', '6.0');
JLoader::registerAlias('JStringNormalise', '\\Joomla\\String\\Normalise', '6.0');
JLoader::registerAlias('JData', '\\Joomla\\Data\\DataObject', '6.0');
JLoader::registerAlias('JDataSet', '\\Joomla\\Data\\DataSet', '6.0');
JLoader::registerAlias('JDataDumpable', '\\Joomla\\Data\\DumpableInterface', '6.0');
JLoader::registerAlias('JApplicationAdministrator', '\\Joomla\\CMS\\Application\\AdministratorApplication', '6.0');
JLoader::registerAlias('JApplicationHelper', '\\Joomla\\CMS\\Application\\ApplicationHelper', '6.0');
JLoader::registerAlias('JApplicationBase', '\\Joomla\\CMS\\Application\\BaseApplication', '6.0');
JLoader::registerAlias('JApplicationCli', '\\Joomla\\CMS\\Application\\CliApplication', '6.0');
JLoader::registerAlias('JApplicationCms', '\\Joomla\\CMS\\Application\\CMSApplication', '6.0');
JLoader::registerAlias('JApplicationDaemon', '\\Joomla\\CMS\\Application\\DaemonApplication', '6.0');
JLoader::registerAlias('JApplicationSite', '\\Joomla\\CMS\\Application\\SiteApplication', '6.0');
JLoader::registerAlias('JApplicationWeb', '\\Joomla\\CMS\\Application\\WebApplication', '6.0');
JLoader::registerAlias('JApplicationWebClient', '\\Joomla\\Application\\Web\\WebClient', '6.0');
JLoader::registerAlias('JDaemon', '\\Joomla\\CMS\\Application\\DaemonApplication', '6.0');
JLoader::registerAlias('JCli', '\\Joomla\\CMS\\Application\\CliApplication', '6.0');
JLoader::registerAlias('JWeb', '\\Joomla\\CMS\\Application\\WebApplication', '4.0');
JLoader::registerAlias('JWebClient', '\\Joomla\\Application\\Web\\WebClient', '4.0');
JLoader::registerAlias('JModelAdmin', '\\Joomla\\CMS\\MVC\\Model\\AdminModel', '6.0');
JLoader::registerAlias('JModelForm', '\\Joomla\\CMS\\MVC\\Model\\FormModel', '6.0');
JLoader::registerAlias('JModelItem', '\\Joomla\\CMS\\MVC\\Model\\ItemModel', '6.0');
JLoader::registerAlias('JModelList', '\\Joomla\\CMS\\MVC\\Model\\ListModel', '6.0');
JLoader::registerAlias('JModelLegacy', '\\Joomla\\CMS\\MVC\\Model\\BaseDatabaseModel', '6.0');
JLoader::registerAlias('JViewCategories', '\\Joomla\\CMS\\MVC\\View\\CategoriesView', '6.0');
JLoader::registerAlias('JViewCategory', '\\Joomla\\CMS\\MVC\\View\\CategoryView', '6.0');
JLoader::registerAlias('JViewCategoryfeed', '\\Joomla\\CMS\\MVC\\View\\CategoryFeedView', '6.0');
JLoader::registerAlias('JViewLegacy', '\\Joomla\\CMS\\MVC\\View\\HtmlView', '6.0');
JLoader::registerAlias('JControllerAdmin', '\\Joomla\\CMS\\MVC\\Controller\\AdminController', '6.0');
JLoader::registerAlias('JControllerLegacy', '\\Joomla\\CMS\\MVC\\Controller\\BaseController', '6.0');
JLoader::registerAlias('JControllerForm', '\\Joomla\\CMS\\MVC\\Controller\\FormController', '6.0');
JLoader::registerAlias('JTableInterface', '\\Joomla\\CMS\\Table\\TableInterface', '6.0');
JLoader::registerAlias('JTable', '\\Joomla\\CMS\\Table\\Table', '6.0');
JLoader::registerAlias('JTableNested', '\\Joomla\\CMS\\Table\\Nested', '6.0');
JLoader::registerAlias('JTableAsset', '\\Joomla\\CMS\\Table\\Asset', '6.0');
JLoader::registerAlias('JTableExtension', '\\Joomla\\CMS\\Table\\Extension', '6.0');
JLoader::registerAlias('JTableLanguage', '\\Joomla\\CMS\\Table\\Language', '6.0');
JLoader::registerAlias('JTableUpdate', '\\Joomla\\CMS\\Table\\Update', '6.0');
JLoader::registerAlias('JTableUpdatesite', '\\Joomla\\CMS\\Table\\UpdateSite', '6.0');
JLoader::registerAlias('JTableUser', '\\Joomla\\CMS\\Table\\User', '6.0');
JLoader::registerAlias('JTableUsergroup', '\\Joomla\\CMS\\Table\\Usergroup', '6.0');
JLoader::registerAlias('JTableViewlevel', '\\Joomla\\CMS\\Table\\ViewLevel', '6.0');
JLoader::registerAlias('JTableContenthistory', '\\Joomla\\CMS\\Table\\ContentHistory', '6.0');
JLoader::registerAlias('JTableContenttype', '\\Joomla\\CMS\\Table\\ContentType', '6.0');
JLoader::registerAlias('JTableCorecontent', '\\Joomla\\CMS\\Table\\CoreContent', '6.0');
JLoader::registerAlias('JTableUcm', '\\Joomla\\CMS\\Table\\Ucm', '6.0');
JLoader::registerAlias('JTableCategory', '\\Joomla\\CMS\\Table\\Category', '6.0');
JLoader::registerAlias('JTableContent', '\\Joomla\\CMS\\Table\\Content', '6.0');
JLoader::registerAlias('JTableMenu', '\\Joomla\\CMS\\Table\\Menu', '6.0');
JLoader::registerAlias('JTableMenuType', '\\Joomla\\CMS\\Table\\MenuType', '6.0');
JLoader::registerAlias('JTableModule', '\\Joomla\\CMS\\Table\\Module', '6.0');
JLoader::registerAlias('JAccess', '\\Joomla\\CMS\\Access\\Access', '6.0');
JLoader::registerAlias('JAccessRule', '\\Joomla\\CMS\\Access\\Rule', '6.0');
JLoader::registerAlias('JAccessRules', '\\Joomla\\CMS\\Access\\Rules', '6.0');
JLoader::registerAlias('JAccessExceptionNotallowed', '\\Joomla\\CMS\\Access\\Exception\\NotAllowed', '6.0');
JLoader::registerAlias('JRule', '\\Joomla\\CMS\\Access\\Rule', '6.0');
JLoader::registerAlias('JRules', '\\Joomla\\CMS\\Access\\Rules', '6.0');
JLoader::registerAlias('JHelp', '\\Joomla\\CMS\\Help\\Help', '6.0');
JLoader::registerAlias('JCaptcha', '\\Joomla\\CMS\\Captcha\\Captcha', '6.0');
JLoader::registerAlias('JLanguageAssociations', '\\Joomla\\CMS\\Language\\Associations', '6.0');
JLoader::registerAlias('JLanguage', '\\Joomla\\CMS\\Language\\Language', '6.0');
JLoader::registerAlias('JLanguageHelper', '\\Joomla\\CMS\\Language\\LanguageHelper', '6.0');
JLoader::registerAlias('JLanguageMultilang', '\\Joomla\\CMS\\Language\\Multilanguage', '6.0');
JLoader::registerAlias('JText', '\\Joomla\\CMS\\Language\\Text', '6.0');
JLoader::registerAlias('JLanguageTransliterate', '\\Joomla\\CMS\\Language\\Transliterate', '6.0');
JLoader::registerAlias('JComponentHelper', '\\Joomla\\CMS\\Component\\ComponentHelper', '6.0');
JLoader::registerAlias('JComponentRecord', '\\Joomla\\CMS\\Component\\ComponentRecord', '6.0');
JLoader::registerAlias('JComponentExceptionMissing', '\\Joomla\\CMS\\Component\\Exception\\MissingComponentException', '6.0');
JLoader::registerAlias('JComponentRouterBase', '\\Joomla\\CMS\\Component\\Router\\RouterBase', '6.0');
JLoader::registerAlias('JComponentRouterInterface', '\\Joomla\\CMS\\Component\\Router\\RouterInterface', '6.0');
JLoader::registerAlias('JComponentRouterLegacy', '\\Joomla\\CMS\\Component\\Router\\RouterLegacy', '6.0');
JLoader::registerAlias('JComponentRouterView', '\\Joomla\\CMS\\Component\\Router\\RouterView', '6.0');
JLoader::registerAlias('JComponentRouterViewconfiguration', '\\Joomla\\CMS\\Component\\Router\\RouterViewConfiguration', '6.0');
JLoader::registerAlias('JComponentRouterRulesMenu', '\\Joomla\\CMS\\Component\\Router\\Rules\\MenuRules', '6.0');
JLoader::registerAlias('JComponentRouterRulesNomenu', '\\Joomla\\CMS\\Component\\Router\\Rules\\NomenuRules', '6.0');
JLoader::registerAlias('JComponentRouterRulesInterface', '\\Joomla\\CMS\\Component\\Router\\Rules\\RulesInterface', '6.0');
JLoader::registerAlias('JComponentRouterRulesStandard', '\\Joomla\\CMS\\Component\\Router\\Rules\\StandardRules', '6.0');
JLoader::registerAlias('JEditor', '\\Joomla\\CMS\\Editor\\Editor', '6.0');
JLoader::registerAlias('JErrorPage', '\\Joomla\\CMS\\Exception\\ExceptionHandler', '6.0');
JLoader::registerAlias('JAuthenticationHelper', '\\Joomla\\CMS\\Helper\\AuthenticationHelper', '6.0');
JLoader::registerAlias('JHelper', '\\Joomla\\CMS\\Helper\\CMSHelper', '6.0');
JLoader::registerAlias('JHelperContent', '\\Joomla\\CMS\\Helper\\ContentHelper', '6.0');
JLoader::registerAlias('JLibraryHelper', '\\Joomla\\CMS\\Helper\\LibraryHelper', '6.0');
JLoader::registerAlias('JHelperMedia', '\\Joomla\\CMS\\Helper\\MediaHelper', '6.0');
JLoader::registerAlias('JModuleHelper', '\\Joomla\\CMS\\Helper\\ModuleHelper', '6.0');
JLoader::registerAlias('JHelperRoute', '\\Joomla\\CMS\\Helper\\RouteHelper', '6.0');
JLoader::registerAlias('JHelperTags', '\\Joomla\\CMS\\Helper\\TagsHelper', '6.0');
JLoader::registerAlias('JHelperUsergroups', '\\Joomla\\CMS\\Helper\\UserGroupsHelper', '6.0');
JLoader::registerAlias('JLayoutBase', '\\Joomla\\CMS\\Layout\\BaseLayout', '6.0');
JLoader::registerAlias('JLayoutFile', '\\Joomla\\CMS\\Layout\\FileLayout', '6.0');
JLoader::registerAlias('JLayoutHelper', '\\Joomla\\CMS\\Layout\\LayoutHelper', '6.0');
JLoader::registerAlias('JLayout', '\\Joomla\\CMS\\Layout\\LayoutInterface', '6.0');
JLoader::registerAlias('JResponseJson', '\\Joomla\\CMS\\Response\\JsonResponse', '6.0');
JLoader::registerAlias('JPlugin', '\\Joomla\\CMS\\Plugin\\CMSPlugin', '6.0');
JLoader::registerAlias('JPluginHelper', '\\Joomla\\CMS\\Plugin\\PluginHelper', '6.0');
JLoader::registerAlias('JMenu', '\\Joomla\\CMS\\Menu\\AbstractMenu', '6.0');
JLoader::registerAlias('JMenuAdministrator', '\\Joomla\\CMS\\Menu\\AdministratorMenu', '6.0');
JLoader::registerAlias('JMenuItem', '\\Joomla\\CMS\\Menu\\MenuItem', '6.0');
JLoader::registerAlias('JMenuSite', '\\Joomla\\CMS\\Menu\\SiteMenu', '6.0');
JLoader::registerAlias('JPagination', '\\Joomla\\CMS\\Pagination\\Pagination', '6.0');
JLoader::registerAlias('JPaginationObject', '\\Joomla\\CMS\\Pagination\\PaginationObject', '6.0');
JLoader::registerAlias('JPathway', '\\Joomla\\CMS\\Pathway\\Pathway', '6.0');
JLoader::registerAlias('JPathwaySite', '\\Joomla\\CMS\\Pathway\\SitePathway', '6.0');
JLoader::registerAlias('JSchemaChangeitem', '\\Joomla\\CMS\\Schema\\ChangeItem', '6.0');
JLoader::registerAlias('JSchemaChangeset', '\\Joomla\\CMS\\Schema\\ChangeSet', '6.0');
JLoader::registerAlias('JSchemaChangeitemMysql', '\\Joomla\\CMS\\Schema\\ChangeItem\\MysqlChangeItem', '6.0');
JLoader::registerAlias('JSchemaChangeitemPostgresql', '\\Joomla\\CMS\\Schema\\ChangeItem\\PostgresqlChangeItem', '6.0');
JLoader::registerAlias('JUcm', '\\Joomla\\CMS\\UCM\\UCM', '6.0');
JLoader::registerAlias('JUcmBase', '\\Joomla\\CMS\\UCM\\UCMBase', '6.0');
JLoader::registerAlias('JUcmContent', '\\Joomla\\CMS\\UCM\\UCMContent', '6.0');
JLoader::registerAlias('JUcmType', '\\Joomla\\CMS\\UCM\\UCMType', '6.0');
JLoader::registerAlias('JToolbar', '\\Joomla\\CMS\\Toolbar\\Toolbar', '6.0');
JLoader::registerAlias('JToolbarButton', '\\Joomla\\CMS\\Toolbar\\ToolbarButton', '6.0');
JLoader::registerAlias('JToolbarButtonConfirm', '\\Joomla\\CMS\\Toolbar\\Button\\ConfirmButton', '6.0');
JLoader::registerAlias('JToolbarButtonCustom', '\\Joomla\\CMS\\Toolbar\\Button\\CustomButton', '6.0');
JLoader::registerAlias('JToolbarButtonHelp', '\\Joomla\\CMS\\Toolbar\\Button\\HelpButton', '6.0');
JLoader::registerAlias('JToolbarButtonLink', '\\Joomla\\CMS\\Toolbar\\Button\\LinkButton', '6.0');
JLoader::registerAlias('JToolbarButtonPopup', '\\Joomla\\CMS\\Toolbar\\Button\\PopupButton', '6.0');
JLoader::registerAlias('JToolbarButtonSeparator', '\\Joomla\\CMS\\Toolbar\\Button\\SeparatorButton', '6.0');
JLoader::registerAlias('JToolbarButtonStandard', '\\Joomla\\CMS\\Toolbar\\Button\\StandardButton', '6.0');
JLoader::registerAlias('JToolbarHelper', '\\Joomla\\CMS\\Toolbar\\ToolbarHelper', '6.0');
JLoader::registerAlias('JButton', '\\Joomla\\CMS\\Toolbar\\ToolbarButton', '6.0');
JLoader::registerAlias('JVersion', '\\Joomla\\CMS\\Version', '6.0');
JLoader::registerAlias('JAuthentication', '\\Joomla\\CMS\\Authentication\\Authentication', '6.0');
JLoader::registerAlias('JAuthenticationResponse', '\\Joomla\\CMS\\Authentication\\AuthenticationResponse', '6.0');
JLoader::registerAlias('JBrowser', '\\Joomla\\CMS\\Environment\\Browser', '6.0');
JLoader::registerAlias('JAssociationExtensionInterface', '\\Joomla\\CMS\\Association\\AssociationExtensionInterface', '6.0');
JLoader::registerAlias('JAssociationExtensionHelper', '\\Joomla\\CMS\\Association\\AssociationExtensionHelper', '6.0');
JLoader::registerAlias('JDocument', '\\Joomla\\CMS\\Document\\Document', '6.0');
JLoader::registerAlias('JDocumentError', '\\Joomla\\CMS\\Document\\ErrorDocument', '6.0');
JLoader::registerAlias('JDocumentFeed', '\\Joomla\\CMS\\Document\\FeedDocument', '6.0');
JLoader::registerAlias('JDocumentHtml', '\\Joomla\\CMS\\Document\\HtmlDocument', '6.0');
JLoader::registerAlias('JDocumentImage', '\\Joomla\\CMS\\Document\\ImageDocument', '6.0');
JLoader::registerAlias('JDocumentJson', '\\Joomla\\CMS\\Document\\JsonDocument', '6.0');
JLoader::registerAlias('JDocumentOpensearch', '\\Joomla\\CMS\\Document\\OpensearchDocument', '6.0');
JLoader::registerAlias('JDocumentRaw', '\\Joomla\\CMS\\Document\\RawDocument', '6.0');
JLoader::registerAlias('JDocumentRenderer', '\\Joomla\\CMS\\Document\\DocumentRenderer', '6.0');
JLoader::registerAlias('JDocumentXml', '\\Joomla\\CMS\\Document\\XmlDocument', '6.0');
JLoader::registerAlias('JDocumentRendererFeedAtom', '\\Joomla\\CMS\\Document\\Renderer\\Feed\\AtomRenderer', '6.0');
JLoader::registerAlias('JDocumentRendererFeedRss', '\\Joomla\\CMS\\Document\\Renderer\\Feed\\RssRenderer', '6.0');
JLoader::registerAlias('JDocumentRendererHtmlComponent', '\\Joomla\\CMS\\Document\\Renderer\\Html\\ComponentRenderer', '6.0');
JLoader::registerAlias('JDocumentRendererHtmlHead', '\\Joomla\\CMS\\Document\\Renderer\\Html\\HeadRenderer', '6.0');
JLoader::registerAlias('JDocumentRendererHtmlMessage', '\\Joomla\\CMS\\Document\\Renderer\\Html\\MessageRenderer', '6.0');
JLoader::registerAlias('JDocumentRendererHtmlModule', '\\Joomla\\CMS\\Document\\Renderer\\Html\\ModuleRenderer', '6.0');
JLoader::registerAlias('JDocumentRendererHtmlModules', '\\Joomla\\CMS\\Document\\Renderer\\Html\\ModulesRenderer', '6.0');
JLoader::registerAlias('JDocumentRendererAtom', '\\Joomla\\CMS\\Document\\Renderer\\Feed\\AtomRenderer', '4.0');
JLoader::registerAlias('JDocumentRendererRSS', '\\Joomla\\CMS\\Document\\Renderer\\Feed\\RssRenderer', '4.0');
JLoader::registerAlias('JDocumentRendererComponent', '\\Joomla\\CMS\\Document\\Renderer\\Html\\ComponentRenderer', '4.0');
JLoader::registerAlias('JDocumentRendererHead', '\\Joomla\\CMS\\Document\\Renderer\\Html\\HeadRenderer', '4.0');
JLoader::registerAlias('JDocumentRendererMessage', '\\Joomla\\CMS\\Document\\Renderer\\Html\\MessageRenderer', '4.0');
JLoader::registerAlias('JDocumentRendererModule', '\\Joomla\\CMS\\Document\\Renderer\\Html\\ModuleRenderer', '4.0');
JLoader::registerAlias('JDocumentRendererModules', '\\Joomla\\CMS\\Document\\Renderer\\Html\\ModulesRenderer', '4.0');
JLoader::registerAlias('JFeedEnclosure', '\\Joomla\\CMS\\Document\\Feed\\FeedEnclosure', '6.0');
JLoader::registerAlias('JFeedImage', '\\Joomla\\CMS\\Document\\Feed\\FeedImage', '6.0');
JLoader::registerAlias('JFeedItem', '\\Joomla\\CMS\\Document\\Feed\\FeedItem', '6.0');
JLoader::registerAlias('JOpenSearchImage', '\\Joomla\\CMS\\Document\\Opensearch\\OpensearchImage', '6.0');
JLoader::registerAlias('JOpenSearchUrl', '\\Joomla\\CMS\\Document\\Opensearch\\OpensearchUrl', '6.0');
JLoader::registerAlias('JFilterInput', '\\Joomla\\CMS\\Filter\\InputFilter', '6.0');
JLoader::registerAlias('JFilterOutput', '\\Joomla\\CMS\\Filter\\OutputFilter', '6.0');
JLoader::registerAlias('JHttp', '\\Joomla\\CMS\\Http\\Http', '6.0');
JLoader::registerAlias('JHttpFactory', '\\Joomla\\CMS\\Http\\HttpFactory', '6.0');
JLoader::registerAlias('JHttpResponse', '\\Joomla\\CMS\\Http\\Response', '6.0');
JLoader::registerAlias('JHttpTransport', '\\Joomla\\CMS\\Http\\TransportInterface', '6.0');
JLoader::registerAlias('JHttpTransportCurl', '\\Joomla\\CMS\\Http\\Transport\\CurlTransport', '6.0');
JLoader::registerAlias('JHttpTransportSocket', '\\Joomla\\CMS\\Http\\Transport\\SocketTransport', '6.0');
JLoader::registerAlias('JHttpTransportStream', '\\Joomla\\CMS\\Http\\Transport\\StreamTransport', '6.0');
JLoader::registerAlias('JInstaller', '\\Joomla\\CMS\\Installer\\Installer', '6.0');
JLoader::registerAlias('JInstallerAdapter', '\\Joomla\\CMS\\Installer\\InstallerAdapter', '6.0');
JLoader::registerAlias('JInstallerExtension', '\\Joomla\\CMS\\Installer\\InstallerExtension', '6.0');
JLoader::registerAlias('JExtension', '\\Joomla\\CMS\\Installer\\InstallerExtension', '6.0');
JLoader::registerAlias('JInstallerHelper', '\\Joomla\\CMS\\Installer\\InstallerHelper', '6.0');
JLoader::registerAlias('JInstallerScript', '\\Joomla\\CMS\\Installer\\InstallerScript', '6.0');
JLoader::registerAlias('JInstallerManifest', '\\Joomla\\CMS\\Installer\\Manifest', '6.0');
JLoader::registerAlias('JInstallerAdapterComponent', '\\Joomla\\CMS\\Installer\\Adapter\\ComponentAdapter', '6.0');
JLoader::registerAlias('JInstallerComponent', '\\Joomla\\CMS\\Installer\\Adapter\\ComponentAdapter', '6.0');
JLoader::registerAlias('JInstallerAdapterFile', '\\Joomla\\CMS\\Installer\\Adapter\\FileAdapter', '6.0');
JLoader::registerAlias('JInstallerFile', '\\Joomla\\CMS\\Installer\\Adapter\\FileAdapter', '6.0');
JLoader::registerAlias('JInstallerAdapterLanguage', '\\Joomla\\CMS\\Installer\\Adapter\\LanguageAdapter', '6.0');
JLoader::registerAlias('JInstallerLanguage', '\\Joomla\\CMS\\Installer\\Adapter\\LanguageAdapter', '6.0');
JLoader::registerAlias('JInstallerAdapterLibrary', '\\Joomla\\CMS\\Installer\\Adapter\\LibraryAdapter', '6.0');
JLoader::registerAlias('JInstallerLibrary', '\\Joomla\\CMS\\Installer\\Adapter\\LibraryAdapter', '6.0');
JLoader::registerAlias('JInstallerAdapterModule', '\\Joomla\\CMS\\Installer\\Adapter\\ModuleAdapter', '6.0');
JLoader::registerAlias('JInstallerModule', '\\Joomla\\CMS\\Installer\\Adapter\\ModuleAdapter', '6.0');
JLoader::registerAlias('JInstallerAdapterPackage', '\\Joomla\\CMS\\Installer\\Adapter\\PackageAdapter', '6.0');
JLoader::registerAlias('JInstallerPackage', '\\Joomla\\CMS\\Installer\\Adapter\\PackageAdapter', '6.0');
JLoader::registerAlias('JInstallerAdapterPlugin', '\\Joomla\\CMS\\Installer\\Adapter\\PluginAdapter', '6.0');
JLoader::registerAlias('JInstallerPlugin', '\\Joomla\\CMS\\Installer\\Adapter\\PluginAdapter', '6.0');
JLoader::registerAlias('JInstallerAdapterTemplate', '\\Joomla\\CMS\\Installer\\Adapter\\TemplateAdapter', '6.0');
JLoader::registerAlias('JInstallerTemplate', '\\Joomla\\CMS\\Installer\\Adapter\\TemplateAdapter', '6.0');
JLoader::registerAlias('JInstallerManifestLibrary', '\\Joomla\\CMS\\Installer\\Manifest\\LibraryManifest', '6.0');
JLoader::registerAlias('JInstallerManifestPackage', '\\Joomla\\CMS\\Installer\\Manifest\\PackageManifest', '6.0');
JLoader::registerAlias('JRouterAdministrator', '\\Joomla\\CMS\\Router\\AdministratorRouter', '6.0');
JLoader::registerAlias('JRoute', '\\Joomla\\CMS\\Router\\Route', '6.0');
JLoader::registerAlias('JRouter', '\\Joomla\\CMS\\Router\\Router', '6.0');
JLoader::registerAlias('JRouterSite', '\\Joomla\\CMS\\Router\\SiteRouter', '6.0');
JLoader::registerAlias('JCategories', '\\Joomla\\CMS\\Categories\\Categories', '6.0');
JLoader::registerAlias('JCategoryNode', '\\Joomla\\CMS\\Categories\\CategoryNode', '6.0');
JLoader::registerAlias('JDate', '\\Joomla\\CMS\\Date\\Date', '6.0');
JLoader::registerAlias('JLog', '\\Joomla\\CMS\\Log\\Log', '6.0');
JLoader::registerAlias('JLogEntry', '\\Joomla\\CMS\\Log\\LogEntry', '6.0');
JLoader::registerAlias('JLogLogger', '\\Joomla\\CMS\\Log\\Logger', '6.0');
JLoader::registerAlias('JLogger', '\\Joomla\\CMS\\Log\\Logger', '6.0');
JLoader::registerAlias('JLogLoggerCallback', '\\Joomla\\CMS\\Log\\Logger\\CallbackLogger', '6.0');
JLoader::registerAlias('JLogLoggerDatabase', '\\Joomla\\CMS\\Log\\Logger\\DatabaseLogger', '6.0');
JLoader::registerAlias('JLogLoggerEcho', '\\Joomla\\CMS\\Log\\Logger\\EchoLogger', '6.0');
JLoader::registerAlias('JLogLoggerFormattedtext', '\\Joomla\\CMS\\Log\\Logger\\FormattedtextLogger', '6.0');
JLoader::registerAlias('JLogLoggerMessagequeue', '\\Joomla\\CMS\\Log\\Logger\\MessagequeueLogger', '6.0');
JLoader::registerAlias('JLogLoggerSyslog', '\\Joomla\\CMS\\Log\\Logger\\SyslogLogger', '6.0');
JLoader::registerAlias('JLogLoggerW3c', '\\Joomla\\CMS\\Log\\Logger\\W3cLogger', '6.0');
JLoader::registerAlias('JProfiler', '\\Joomla\\CMS\\Profiler\\Profiler', '6.0');
JLoader::registerAlias('JUri', '\\Joomla\\CMS\\Uri\\Uri', '6.0');
JLoader::registerAlias('JCache', '\\Joomla\\CMS\\Cache\\Cache', '6.0');
JLoader::registerAlias('JCacheController', '\\Joomla\\CMS\\Cache\\CacheController', '6.0');
JLoader::registerAlias('JCacheStorage', '\\Joomla\\CMS\\Cache\\CacheStorage', '6.0');
JLoader::registerAlias('JCacheControllerCallback', '\\Joomla\\CMS\\Cache\\Controller\\CallbackController', '6.0');
JLoader::registerAlias('JCacheControllerOutput', '\\Joomla\\CMS\\Cache\\Controller\\OutputController', '6.0');
JLoader::registerAlias('JCacheControllerPage', '\\Joomla\\CMS\\Cache\\Controller\\PageController', '6.0');
JLoader::registerAlias('JCacheControllerView', '\\Joomla\\CMS\\Cache\\Controller\\ViewController', '6.0');
JLoader::registerAlias('JCacheStorageApcu', '\\Joomla\\CMS\\Cache\\Storage\\ApcuStorage', '6.0');
JLoader::registerAlias('JCacheStorageHelper', '\\Joomla\\CMS\\Cache\\Storage\\CacheStorageHelper', '6.0');
JLoader::registerAlias('JCacheStorageFile', '\\Joomla\\CMS\\Cache\\Storage\\FileStorage', '6.0');
JLoader::registerAlias('JCacheStorageMemcached', '\\Joomla\\CMS\\Cache\\Storage\\MemcachedStorage', '6.0');
JLoader::registerAlias('JCacheStorageRedis', '\\Joomla\\CMS\\Cache\\Storage\\RedisStorage', '6.0');
JLoader::registerAlias('JCacheException', '\\Joomla\\CMS\\Cache\\Exception\\CacheExceptionInterface', '6.0');
JLoader::registerAlias('JCacheExceptionConnecting', '\\Joomla\\CMS\\Cache\\Exception\\CacheConnectingException', '6.0');
JLoader::registerAlias('JCacheExceptionUnsupported', '\\Joomla\\CMS\\Cache\\Exception\\UnsupportedCacheException', '6.0');
JLoader::registerAlias('JSession', '\\Joomla\\CMS\\Session\\Session', '6.0');
JLoader::registerAlias('JUser', '\\Joomla\\CMS\\User\\User', '6.0');
JLoader::registerAlias('JUserHelper', '\\Joomla\\CMS\\User\\UserHelper', '6.0');
JLoader::registerAlias('JForm', '\\Joomla\\CMS\\Form\\Form', '6.0');
JLoader::registerAlias('JFormField', '\\Joomla\\CMS\\Form\\FormField', '6.0');
JLoader::registerAlias('JFormHelper', '\\Joomla\\CMS\\Form\\FormHelper', '6.0');
JLoader::registerAlias('JFormRule', '\\Joomla\\CMS\\Form\\FormRule', '6.0');
JLoader::registerAlias('JFormFieldAccessLevel', '\\Joomla\\CMS\\Form\\Field\\AccesslevelField', '6.0');
JLoader::registerAlias('JFormFieldAliastag', '\\Joomla\\CMS\\Form\\Field\\AliastagField', '6.0');
JLoader::registerAlias('JFormFieldAuthor', '\\Joomla\\CMS\\Form\\Field\\AuthorField', '6.0');
JLoader::registerAlias('JFormFieldCacheHandler', '\\Joomla\\CMS\\Form\\Field\\CachehandlerField', '6.0');
JLoader::registerAlias('JFormFieldCalendar', '\\Joomla\\CMS\\Form\\Field\\CalendarField', '6.0');
JLoader::registerAlias('JFormFieldCaptcha', '\\Joomla\\CMS\\Form\\Field\\CaptchaField', '6.0');
JLoader::registerAlias('JFormFieldCategory', '\\Joomla\\CMS\\Form\\Field\\CategoryField', '6.0');
JLoader::registerAlias('JFormFieldCheckbox', '\\Joomla\\CMS\\Form\\Field\\CheckboxField', '6.0');
JLoader::registerAlias('JFormFieldCheckboxes', '\\Joomla\\CMS\\Form\\Field\\CheckboxesField', '6.0');
JLoader::registerAlias('JFormFieldChromeStyle', '\\Joomla\\CMS\\Form\\Field\\ChromestyleField', '6.0');
JLoader::registerAlias('JFormFieldColor', '\\Joomla\\CMS\\Form\\Field\\ColorField', '6.0');
JLoader::registerAlias('JFormFieldCombo', '\\Joomla\\CMS\\Form\\Field\\ComboField', '6.0');
JLoader::registerAlias('JFormFieldComponentlayout', '\\Joomla\\CMS\\Form\\Field\\ComponentlayoutField', '6.0');
JLoader::registerAlias('JFormFieldComponents', '\\Joomla\\CMS\\Form\\Field\\ComponentsField', '6.0');
JLoader::registerAlias('JFormFieldContenthistory', '\\Joomla\\CMS\\Form\\Field\\ContenthistoryField', '6.0');
JLoader::registerAlias('JFormFieldContentlanguage', '\\Joomla\\CMS\\Form\\Field\\ContentlanguageField', '6.0');
JLoader::registerAlias('JFormFieldContenttype', '\\Joomla\\CMS\\Form\\Field\\ContenttypeField', '6.0');
JLoader::registerAlias('JFormFieldDatabaseConnection', '\\Joomla\\CMS\\Form\\Field\\DatabaseconnectionField', '6.0');
JLoader::registerAlias('JFormFieldEditor', '\\Joomla\\CMS\\Form\\Field\\EditorField', '6.0');
JLoader::registerAlias('JFormFieldEMail', '\\Joomla\\CMS\\Form\\Field\\EmailField', '6.0');
JLoader::registerAlias('JFormFieldFile', '\\Joomla\\CMS\\Form\\Field\\FileField', '6.0');
JLoader::registerAlias('JFormFieldFileList', '\\Joomla\\CMS\\Form\\Field\\FilelistField', '6.0');
JLoader::registerAlias('JFormFieldFolderList', '\\Joomla\\CMS\\Form\\Field\\FolderlistField', '6.0');
JLoader::registerAlias('JFormFieldFrontend_Language', '\\Joomla\\CMS\\Form\\Field\\FrontendlanguageField', '6.0');
JLoader::registerAlias('JFormFieldGroupedList', '\\Joomla\\CMS\\Form\\Field\\GroupedlistField', '6.0');
JLoader::registerAlias('JFormFieldHeadertag', '\\Joomla\\CMS\\Form\\Field\\HeadertagField', '6.0');
JLoader::registerAlias('JFormFieldHidden', '\\Joomla\\CMS\\Form\\Field\\HiddenField', '6.0');
JLoader::registerAlias('JFormFieldImageList', '\\Joomla\\CMS\\Form\\Field\\ImagelistField', '6.0');
JLoader::registerAlias('JFormFieldInteger', '\\Joomla\\CMS\\Form\\Field\\IntegerField', '6.0');
JLoader::registerAlias('JFormFieldLanguage', '\\Joomla\\CMS\\Form\\Field\\LanguageField', '6.0');
JLoader::registerAlias('JFormFieldLastvisitDateRange', '\\Joomla\\CMS\\Form\\Field\\LastvisitdaterangeField', '6.0');
JLoader::registerAlias('JFormFieldLimitbox', '\\Joomla\\CMS\\Form\\Field\\LimitboxField', '6.0');
JLoader::registerAlias('JFormFieldList', '\\Joomla\\CMS\\Form\\Field\\ListField', '6.0');
JLoader::registerAlias('JFormFieldMedia', '\\Joomla\\CMS\\Form\\Field\\MediaField', '6.0');
JLoader::registerAlias('JFormFieldMenu', '\\Joomla\\CMS\\Form\\Field\\MenuField', '6.0');
JLoader::registerAlias('JFormFieldMenuitem', '\\Joomla\\CMS\\Form\\Field\\MenuitemField', '6.0');
JLoader::registerAlias('JFormFieldMeter', '\\Joomla\\CMS\\Form\\Field\\MeterField', '6.0');
JLoader::registerAlias('JFormFieldModulelayout', '\\Joomla\\CMS\\Form\\Field\\ModulelayoutField', '6.0');
JLoader::registerAlias('JFormFieldModuleOrder', '\\Joomla\\CMS\\Form\\Field\\ModuleorderField', '6.0');
JLoader::registerAlias('JFormFieldModulePosition', '\\Joomla\\CMS\\Form\\Field\\ModulepositionField', '6.0');
JLoader::registerAlias('JFormFieldModuletag', '\\Joomla\\CMS\\Form\\Field\\ModuletagField', '6.0');
JLoader::registerAlias('JFormFieldNote', '\\Joomla\\CMS\\Form\\Field\\NoteField', '6.0');
JLoader::registerAlias('JFormFieldNumber', '\\Joomla\\CMS\\Form\\Field\\NumberField', '6.0');
JLoader::registerAlias('JFormFieldOrdering', '\\Joomla\\CMS\\Form\\Field\\OrderingField', '6.0');
JLoader::registerAlias('JFormFieldPassword', '\\Joomla\\CMS\\Form\\Field\\PasswordField', '6.0');
JLoader::registerAlias('JFormFieldPlugins', '\\Joomla\\CMS\\Form\\Field\\PluginsField', '6.0');
JLoader::registerAlias('JFormFieldPlugin_Status', '\\Joomla\\CMS\\Form\\Field\\PluginstatusField', '6.0');
JLoader::registerAlias('JFormFieldPredefinedList', '\\Joomla\\CMS\\Form\\Field\\PredefinedListField', '6.0');
JLoader::registerAlias('JFormFieldRadio', '\\Joomla\\CMS\\Form\\Field\\RadioField', '6.0');
JLoader::registerAlias('JFormFieldRange', '\\Joomla\\CMS\\Form\\Field\\RangeField', '6.0');
JLoader::registerAlias('JFormFieldRedirect_Status', '\\Joomla\\CMS\\Form\\Field\\RedirectStatusField', '6.0');
JLoader::registerAlias('JFormFieldRegistrationDateRange', '\\Joomla\\CMS\\Form\\Field\\RegistrationdaterangeField', '6.0');
JLoader::registerAlias('JFormFieldRules', '\\Joomla\\CMS\\Form\\Field\\RulesField', '6.0');
JLoader::registerAlias('JFormFieldSessionHandler', '\\Joomla\\CMS\\Form\\Field\\SessionhandlerField', '6.0');
JLoader::registerAlias('JFormFieldSpacer', '\\Joomla\\CMS\\Form\\Field\\SpacerField', '6.0');
JLoader::registerAlias('JFormFieldSQL', '\\Joomla\\CMS\\Form\\Field\\SqlField', '6.0');
JLoader::registerAlias('JFormFieldStatus', '\\Joomla\\CMS\\Form\\Field\\StatusField', '6.0');
JLoader::registerAlias('JFormFieldSubform', '\\Joomla\\CMS\\Form\\Field\\SubformField', '6.0');
JLoader::registerAlias('JFormFieldTag', '\\Joomla\\CMS\\Form\\Field\\TagField', '6.0');
JLoader::registerAlias('JFormFieldTel', '\\Joomla\\CMS\\Form\\Field\\TelephoneField', '6.0');
JLoader::registerAlias('JFormFieldTemplatestyle', '\\Joomla\\CMS\\Form\\Field\\TemplatestyleField', '6.0');
JLoader::registerAlias('JFormFieldText', '\\Joomla\\CMS\\Form\\Field\\TextField', '6.0');
JLoader::registerAlias('JFormFieldTextarea', '\\Joomla\\CMS\\Form\\Field\\TextareaField', '6.0');
JLoader::registerAlias('JFormFieldTimezone', '\\Joomla\\CMS\\Form\\Field\\TimezoneField', '6.0');
JLoader::registerAlias('JFormFieldUrl', '\\Joomla\\CMS\\Form\\Field\\UrlField', '6.0');
JLoader::registerAlias('JFormFieldUserActive', '\\Joomla\\CMS\\Form\\Field\\UseractiveField', '6.0');
JLoader::registerAlias('JFormFieldUserGroupList', '\\Joomla\\CMS\\Form\\Field\\UsergrouplistField', '6.0');
JLoader::registerAlias('JFormFieldUserState', '\\Joomla\\CMS\\Form\\Field\\UserstateField', '6.0');
JLoader::registerAlias('JFormFieldUser', '\\Joomla\\CMS\\Form\\Field\\UserField', '6.0');
JLoader::registerAlias('JFormRuleBoolean', '\\Joomla\\CMS\\Form\\Rule\\BooleanRule', '6.0');
JLoader::registerAlias('JFormRuleCalendar', '\\Joomla\\CMS\\Form\\Rule\\CalendarRule', '6.0');
JLoader::registerAlias('JFormRuleCaptcha', '\\Joomla\\CMS\\Form\\Rule\\CaptchaRule', '6.0');
JLoader::registerAlias('JFormRuleColor', '\\Joomla\\CMS\\Form\\Rule\\ColorRule', '6.0');
JLoader::registerAlias('JFormRuleEmail', '\\Joomla\\CMS\\Form\\Rule\\EmailRule', '6.0');
JLoader::registerAlias('JFormRuleEquals', '\\Joomla\\CMS\\Form\\Rule\\EqualsRule', '6.0');
JLoader::registerAlias('JFormRuleNotequals', '\\Joomla\\CMS\\Form\\Rule\\NotequalsRule', '6.0');
JLoader::registerAlias('JFormRuleNumber', '\\Joomla\\CMS\\Form\\Rule\\NumberRule', '6.0');
JLoader::registerAlias('JFormRuleOptions', '\\Joomla\\CMS\\Form\\Rule\\OptionsRule', '6.0');
JLoader::registerAlias('JFormRulePassword', '\\Joomla\\CMS\\Form\\Rule\\PasswordRule', '6.0');
JLoader::registerAlias('JFormRuleRules', '\\Joomla\\CMS\\Form\\Rule\\RulesRule', '6.0');
JLoader::registerAlias('JFormRuleTel', '\\Joomla\\CMS\\Form\\Rule\\TelRule', '6.0');
JLoader::registerAlias('JFormRuleUrl', '\\Joomla\\CMS\\Form\\Rule\\UrlRule', '6.0');
JLoader::registerAlias('JFormRuleUsername', '\\Joomla\\CMS\\Form\\Rule\\UsernameRule', '6.0');
JLoader::registerAlias('JMicrodata', '\\Joomla\\CMS\\Microdata\\Microdata', '6.0');
JLoader::registerAlias('JDatabaseDriver', '\\Joomla\\Database\\DatabaseDriver', '6.0');
JLoader::registerAlias('JDatabaseExporter', '\\Joomla\\Database\\DatabaseExporter', '6.0');
JLoader::registerAlias('JDatabaseFactory', '\\Joomla\\Database\\DatabaseFactory', '6.0');
JLoader::registerAlias('JDatabaseImporter', '\\Joomla\\Database\\DatabaseImporter', '6.0');
JLoader::registerAlias('JDatabaseInterface', '\\Joomla\\Database\\DatabaseInterface', '6.0');
JLoader::registerAlias('JDatabaseIterator', '\\Joomla\\Database\\DatabaseIterator', '6.0');
JLoader::registerAlias('JDatabaseQuery', '\\Joomla\\Database\\DatabaseQuery', '6.0');
JLoader::registerAlias('JDatabaseDriverMysqli', '\\Joomla\\Database\\Mysqli\\MysqliDriver', '6.0');
JLoader::registerAlias('JDatabaseDriverPdo', '\\Joomla\\Database\\Pdo\\PdoDriver', '6.0');
JLoader::registerAlias('JDatabaseDriverPdomysql', '\\Joomla\\Database\\Mysql\\MysqlDriver', '6.0');
JLoader::registerAlias('JDatabaseDriverPgsql', '\\Joomla\\Database\\Pgsql\\PgsqlDriver', '6.0');
JLoader::registerAlias('JDatabaseDriverSqlazure', '\\Joomla\\Database\\Sqlazure\\SqlazureDriver', '6.0');
JLoader::registerAlias('JDatabaseDriverSqlite', '\\Joomla\\Database\\Sqlite\\SqliteDriver', '6.0');
JLoader::registerAlias('JDatabaseDriverSqlsrv', '\\Joomla\\Database\\Sqlsrv\\SqlsrvDriver', '6.0');
JLoader::registerAlias('JDatabaseExceptionConnecting', '\\Joomla\\Database\\Exception\\ConnectionFailureException', '6.0');
JLoader::registerAlias('JDatabaseExceptionExecuting', '\\Joomla\\Database\\Exception\\ExecutionFailureException', '6.0');
JLoader::registerAlias('JDatabaseExceptionUnsupported', '\\Joomla\\Database\\Exception\\UnsupportedAdapterException', '6.0');
JLoader::registerAlias('JDatabaseExporterMysqli', '\\Joomla\\Database\\Mysqli\\MysqliExporter', '6.0');
JLoader::registerAlias('JDatabaseExporterPdomysql', '\\Joomla\\Database\\Mysql\\MysqlExporter', '6.0');
JLoader::registerAlias('JDatabaseExporterPgsql', '\\Joomla\\Database\\Pgsql\\PgsqlExporter', '6.0');
JLoader::registerAlias('JDatabaseImporterMysqli', '\\Joomla\\Database\\Mysqli\\MysqliImporter', '6.0');
JLoader::registerAlias('JDatabaseImporterPdomysql', '\\Joomla\\Database\\Mysql\\MysqlImporter', '6.0');
JLoader::registerAlias('JDatabaseImporterPgsql', '\\Joomla\\Database\\Pgsql\\PgsqlImporter', '6.0');
JLoader::registerAlias('JDatabaseQueryElement', '\\Joomla\\Database\\Query\\QueryElement', '6.0');
JLoader::registerAlias('JDatabaseQueryLimitable', '\\Joomla\\Database\\Query\\LimitableInterface', '6.0');
JLoader::registerAlias('JDatabaseQueryPreparable', '\\Joomla\\Database\\Query\\PreparableInterface', '6.0');
JLoader::registerAlias('JDatabaseQueryMysqli', '\\Joomla\\Database\\Mysqli\\MysqliQuery', '6.0');
JLoader::registerAlias('JDatabaseQueryPdo', '\\Joomla\\Database\\Pdo\\PdoQuery', '6.0');
JLoader::registerAlias('JDatabaseQueryPdomysql', '\\Joomla\\Database\\Mysql\\MysqlQuery', '6.0');
JLoader::registerAlias('JDatabaseQueryPgsql', '\\Joomla\\Database\\Pgsql\\PgsqlQuery', '6.0');
JLoader::registerAlias('JDatabaseQuerySqlazure', '\\Joomla\\Database\\Sqlazure\\SqlazureQuery', '6.0');
JLoader::registerAlias('JDatabaseQuerySqlite', '\\Joomla\\Database\\Sqlite\\SqliteQuery', '6.0');
JLoader::registerAlias('JDatabaseQuerySqlsrv', '\\Joomla\\Database\\Sqlsrv\\SqlsrvQuery', '6.0');
JLoader::registerAlias('JFactory', '\\Joomla\\CMS\\Factory', '6.0');
JLoader::registerAlias('JMail', '\\Joomla\\CMS\\Mail\\Mail', '6.0');
JLoader::registerAlias('JMailHelper', '\\Joomla\\CMS\\Mail\\MailHelper', '6.0');
JLoader::registerAlias('JClientHelper', '\\Joomla\\CMS\\Client\\ClientHelper', '6.0');
JLoader::registerAlias('JClientFtp', '\\Joomla\\CMS\\Client\\FtpClient', '6.0');
JLoader::registerAlias('JFTP', '\\Joomla\\CMS\\Client\\FtpClient', '4.0');
JLoader::registerAlias('JUpdate', '\\Joomla\\CMS\\Updater\\Update', '6.0');
JLoader::registerAlias('JUpdateAdapter', '\\Joomla\\CMS\\Updater\\UpdateAdapter', '6.0');
JLoader::registerAlias('JUpdater', '\\Joomla\\CMS\\Updater\\Updater', '6.0');
JLoader::registerAlias('JUpdaterCollection', '\\Joomla\\CMS\\Updater\\Adapter\\CollectionAdapter', '6.0');
JLoader::registerAlias('JUpdaterExtension', '\\Joomla\\CMS\\Updater\\Adapter\\ExtensionAdapter', '6.0');
JLoader::registerAlias('JCrypt', '\\Joomla\\CMS\\Crypt\\Crypt', '6.0');
JLoader::registerAlias('JCryptCipher', '\\Joomla\\Crypt\\CipherInterface', '6.0');
JLoader::registerAlias('JCryptKey', '\\Joomla\\Crypt\\Key', '6.0');
JLoader::registerAlias('\\Joomla\\CMS\\Crypt\\CipherInterface', '\\Joomla\\Crypt\\CipherInterface', '6.0');
JLoader::registerAlias('\\Joomla\\CMS\\Crypt\\Key', '\\Joomla\\Crypt\\Key', '6.0');
JLoader::registerAlias('JCryptCipherCrypto', '\\Joomla\\CMS\\Crypt\\Cipher\\CryptoCipher', '6.0');
JLoader::registerAlias('JStringPunycode', '\\Joomla\\CMS\\String\\PunycodeHelper', '6.0');
JLoader::registerAlias('JBuffer', '\\Joomla\\CMS\\Utility\\BufferStreamHandler', '6.0');
JLoader::registerAlias('JUtility', '\\Joomla\\CMS\\Utility\\Utility', '6.0');
JLoader::registerAlias('JInputCli', '\\Joomla\\CMS\\Input\\Cli', '6.0');
JLoader::registerAlias('JInputCookie', '\\Joomla\\CMS\\Input\\Cookie', '6.0');
JLoader::registerAlias('JInputFiles', '\\Joomla\\CMS\\Input\\Files', '6.0');
JLoader::registerAlias('JInput', '\\Joomla\\CMS\\Input\\Input', '6.0');
JLoader::registerAlias('JInputJSON', '\\Joomla\\CMS\\Input\\Json', '6.0');
JLoader::registerAlias('JFeed', '\\Joomla\\CMS\\Feed\\Feed', '6.0');
JLoader::registerAlias('JFeedEntry', '\\Joomla\\CMS\\Feed\\FeedEntry', '6.0');
JLoader::registerAlias('JFeedFactory', '\\Joomla\\CMS\\Feed\\FeedFactory', '6.0');
JLoader::registerAlias('JFeedLink', '\\Joomla\\CMS\\Feed\\FeedLink', '6.0');
JLoader::registerAlias('JFeedParser', '\\Joomla\\CMS\\Feed\\FeedParser', '6.0');
JLoader::registerAlias('JFeedPerson', '\\Joomla\\CMS\\Feed\\FeedPerson', '6.0');
JLoader::registerAlias('JFeedParserAtom', '\\Joomla\\CMS\\Feed\\Parser\\AtomParser', '6.0');
JLoader::registerAlias('JFeedParserNamespace', '\\Joomla\\CMS\\Feed\\Parser\\NamespaceParserInterface', '6.0');
JLoader::registerAlias('JFeedParserRss', '\\Joomla\\CMS\\Feed\\Parser\\RssParser', '6.0');
JLoader::registerAlias('JFeedParserRssItunes', '\\Joomla\\CMS\\Feed\\Parser\\Rss\\ItunesRssParser', '6.0');
JLoader::registerAlias('JFeedParserRssMedia', '\\Joomla\\CMS\\Feed\\Parser\\Rss\\MediaRssParser', '6.0');
JLoader::registerAlias('JImage', '\\Joomla\\CMS\\Image\\Image', '6.0');
JLoader::registerAlias('JImageFilter', '\\Joomla\\CMS\\Image\\ImageFilter', '6.0');
JLoader::registerAlias('JImageFilterBackgroundfill', '\\Joomla\\CMS\\Image\\Filter\\Backgroundfill', '6.0');
JLoader::registerAlias('JImageFilterBrightness', '\\Joomla\\CMS\\Image\\Filter\\Brightness', '6.0');
JLoader::registerAlias('JImageFilterContrast', '\\Joomla\\CMS\\Image\\Filter\\Contrast', '6.0');
JLoader::registerAlias('JImageFilterEdgedetect', '\\Joomla\\CMS\\Image\\Filter\\Edgedetect', '6.0');
JLoader::registerAlias('JImageFilterEmboss', '\\Joomla\\CMS\\Image\\Filter\\Emboss', '6.0');
JLoader::registerAlias('JImageFilterNegate', '\\Joomla\\CMS\\Image\\Filter\\Negate', '6.0');
JLoader::registerAlias('JImageFilterSmooth', '\\Joomla\\CMS\\Image\\Filter\\Smooth', '6.0');
JLoader::registerAlias('JObject', '\\Joomla\\CMS\\Object\\CMSObject', '6.0');
JLoader::registerAlias('JExtensionHelper', '\\Joomla\\CMS\\Extension\\ExtensionHelper', '6.0');
JLoader::registerAlias('JHtml', '\\Joomla\\CMS\\HTML\\HTMLHelper', '6.0');
JLoader::registerAlias('\\Joomla\\Application\\Cli\\CliInput', '\\Joomla\\CMS\\Application\\CLI\\CliInput', '6.0');
JLoader::registerAlias('\\Joomla\\Application\\Cli\\CliOutput', '\\Joomla\\CMS\\Application\\CLI\\CliOutput', '6.0');
JLoader::registerAlias('\\Joomla\\Application\\Cli\\ColorStyle', '\\Joomla\\CMS\\Application\\CLI\\ColorStyle', '6.0');
JLoader::registerAlias('\\Joomla\\Application\\Cli\\Output\\Stdout', '\\Joomla\\CMS\\Application\\CLI\\Output\\Stdout', '6.0');
JLoader::registerAlias('\\Joomla\\Application\\Cli\\Output\\Xml', '\\Joomla\\CMS\\Application\\CLI\\Output\\Xml', '6.0');
JLoader::registerAlias(
'\\Joomla\\Application\\Cli\\Output\\Processor\\ColorProcessor',
'\\Joomla\\CMS\\Application\\CLI\\Output\\Processor\\ColorProcessor',
'6.0'
);
JLoader::registerAlias(
'\\Joomla\\Application\\Cli\\Output\\Processor\\ProcessorInterface',
'\\Joomla\\CMS\\Application\\CLI\\Output\\Processor\\ProcessorInterface',
'6.0'
);
JLoader::registerAlias('JFile', '\\Joomla\\CMS\\Filesystem\\File', '6.0');
JLoader::registerAlias('JFolder', '\\Joomla\\CMS\\Filesystem\\Folder', '6.0');
JLoader::registerAlias('JFilesystemHelper', '\\Joomla\\CMS\\Filesystem\\FilesystemHelper', '6.0');
JLoader::registerAlias('JFilesystemPatcher', '\\Joomla\\CMS\\Filesystem\\Patcher', '6.0');
JLoader::registerAlias('JPath', '\\Joomla\\CMS\\Filesystem\\Path', '6.0');
JLoader::registerAlias('JStream', '\\Joomla\\CMS\\Filesystem\\Stream', '6.0');
JLoader::registerAlias('JStreamString', '\\Joomla\\CMS\\Filesystem\\Streams\\StreamString', '6.0');
JLoader::registerAlias('JStringController', '\\Joomla\\CMS\\Filesystem\\Support\\StringController', '6.0');
JLoader::registerAlias('JClassLoader', '\\Joomla\\CMS\\Autoload\\ClassLoader', '6.0');
JLoader::registerAlias('JFormFilterInt_Array', '\\Joomla\\CMS\\Form\\Filter\\IntarrayFilter', '6.0');
JLoader::registerAlias('JAdapter', '\\Joomla\\CMS\\Adapter\\Adapter', '6.0');
JLoader::registerAlias('JAdapterInstance', '\\Joomla\\CMS\\Adapter\\AdapterInstance', '6.0');
JLoader::registerAlias('JHtmlAccess', '\\Joomla\\CMS\\HTML\\Helpers\\Access', '6.0');
JLoader::registerAlias('JHtmlActionsDropdown', '\\Joomla\\CMS\\HTML\\Helpers\\ActionsDropdown', '6.0');
JLoader::registerAlias('JHtmlAdminLanguage', '\\Joomla\\CMS\\HTML\\Helpers\\AdminLanguage', '6.0');
JLoader::registerAlias('JHtmlBehavior', '\\Joomla\\CMS\\HTML\\Helpers\\Behavior', '6.0');
JLoader::registerAlias('JHtmlBootstrap', '\\Joomla\\CMS\\HTML\\Helpers\\Bootstrap', '6.0');
JLoader::registerAlias('JHtmlCategory', '\\Joomla\\CMS\\HTML\\Helpers\\Category', '6.0');
JLoader::registerAlias('JHtmlContent', '\\Joomla\\CMS\\HTML\\Helpers\\Content', '6.0');
JLoader::registerAlias('JHtmlContentlanguage', '\\Joomla\\CMS\\HTML\\Helpers\\ContentLanguage', '6.0');
JLoader::registerAlias('JHtmlDate', '\\Joomla\\CMS\\HTML\\Helpers\\Date', '6.0');
JLoader::registerAlias('JHtmlDebug', '\\Joomla\\CMS\\HTML\\Helpers\\Debug', '6.0');
JLoader::registerAlias('JHtmlDraggablelist', '\\Joomla\\CMS\\HTML\\Helpers\\DraggableList', '6.0');
JLoader::registerAlias('JHtmlDropdown', '\\Joomla\\CMS\\HTML\\Helpers\\Dropdown', '6.0');
JLoader::registerAlias('JHtmlEmail', '\\Joomla\\CMS\\HTML\\Helpers\\Email', '6.0');
JLoader::registerAlias('JHtmlForm', '\\Joomla\\CMS\\HTML\\Helpers\\Form', '6.0');
JLoader::registerAlias('JHtmlFormbehavior', '\\Joomla\\CMS\\HTML\\Helpers\\FormBehavior', '6.0');
JLoader::registerAlias('JHtmlGrid', '\\Joomla\\CMS\\HTML\\Helpers\\Grid', '6.0');
JLoader::registerAlias('JHtmlIcons', '\\Joomla\\CMS\\HTML\\Helpers\\Icons', '6.0');
JLoader::registerAlias('JHtmlJGrid', '\\Joomla\\CMS\\HTML\\Helpers\\JGrid', '6.0');
JLoader::registerAlias('JHtmlJquery', '\\Joomla\\CMS\\HTML\\Helpers\\Jquery', '6.0');
JLoader::registerAlias('JHtmlLinks', '\\Joomla\\CMS\\HTML\\Helpers\\Links', '6.0');
JLoader::registerAlias('JHtmlList', '\\Joomla\\CMS\\HTML\\Helpers\\ListHelper', '6.0');
JLoader::registerAlias('JHtmlMenu', '\\Joomla\\CMS\\HTML\\Helpers\\Menu', '6.0');
JLoader::registerAlias('JHtmlNumber', '\\Joomla\\CMS\\HTML\\Helpers\\Number', '6.0');
JLoader::registerAlias('JHtmlSearchtools', '\\Joomla\\CMS\\HTML\\Helpers\\SearchTools', '6.0');
JLoader::registerAlias('JHtmlSelect', '\\Joomla\\CMS\\HTML\\Helpers\\Select', '6.0');
JLoader::registerAlias('JHtmlSidebar', '\\Joomla\\CMS\\HTML\\Helpers\\Sidebar', '6.0');
JLoader::registerAlias('JHtmlSortableList', '\\Joomla\\CMS\\HTML\\Helpers\\SortableList', '6.0');
JLoader::registerAlias('JHtmlString', '\\Joomla\\CMS\\HTML\\Helpers\\StringHelper', '6.0');
JLoader::registerAlias('JHtmlTag', '\\Joomla\\CMS\\HTML\\Helpers\\Tag', '6.0');
JLoader::registerAlias('JHtmlTel', '\\Joomla\\CMS\\HTML\\Helpers\\Telephone', '6.0');
JLoader::registerAlias('JHtmlUser', '\\Joomla\\CMS\\HTML\\Helpers\\User', '6.0');

View File

@ -0,0 +1,37 @@
<?php
/**
* Joomla! Content Management System
*
* @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
// No direct access.
\defined('_JEXEC') or die;
// Class map of the core extensions
JLoader::registerAlias('ActionLogPlugin', '\\Joomla\\Component\\Actionlogs\\Administrator\\Plugin\\ActionLogPlugin', '5.0');
JLoader::registerAlias('FieldsPlugin', '\\Joomla\\Component\\Fields\\Administrator\\Plugin\\FieldsPlugin', '5.0');
JLoader::registerAlias('FieldsListPlugin', '\\Joomla\\Component\\Fields\\Administrator\\Plugin\\FieldsListPlugin', '5.0');
JLoader::registerAlias('PrivacyExportDomain', '\\Joomla\\Component\\Privacy\\Administrator\\Export\\Domain', '5.0');
JLoader::registerAlias('PrivacyExportField', '\\Joomla\\Component\\Privacy\\Administrator\\Export\\Field', '5.0');
JLoader::registerAlias('PrivacyExportItem', '\\Joomla\\Component\\Privacy\\Administrator\\Export\\Item', '5.0');
JLoader::registerAlias('PrivacyPlugin', '\\Joomla\\Component\\Privacy\\Administrator\\Plugin\\PrivacyPlugin', '5.0');
JLoader::registerAlias('PrivacyRemovalStatus', '\\Joomla\\Component\\Privacy\\Administrator\\Removal\\Status', '5.0');
JLoader::registerAlias('PrivacyTableRequest', '\\Joomla\\Component\\Privacy\\Administrator\\Table\\RequestTable', '5.0');
JLoader::registerAlias('TagsTableTag', '\\Joomla\\Component\\Tags\\Administrator\\Table\\TagTable', '5.0');
JLoader::registerAlias('ContentHelperRoute', '\\Joomla\\Component\\Content\\Site\\Helper\\RouteHelper', '5.0');
JLoader::registerAlias('FinderIndexerAdapter', '\\Joomla\\Component\\Finder\\Administrator\\Indexer\\Adapter', '5.0');
JLoader::registerAlias('FinderIndexerHelper', '\\Joomla\\Component\\Finder\\Administrator\\Indexer\\Helper', '5.0');
JLoader::registerAlias('FinderIndexer', '\\Joomla\\Component\\Finder\\Administrator\\Indexer\\Indexer', '5.0');
JLoader::registerAlias('FinderIndexerParser', '\\Joomla\\Component\\Finder\\Administrator\\Indexer\\Parser', '5.0');
JLoader::registerAlias('FinderIndexerQuery', '\\Joomla\\Component\\Finder\\Administrator\\Indexer\\Query', '5.0');
JLoader::registerAlias('FinderIndexerResult', '\\Joomla\\Component\\Finder\\Administrator\\Indexer\\Result', '5.0');
JLoader::registerAlias('FinderIndexerTaxonomy', '\\Joomla\\Component\\Finder\\Administrator\\Indexer\\Taxonomy', '5.0');
JLoader::registerAlias('FinderIndexerToken', '\\Joomla\\Component\\Finder\\Administrator\\Indexer\\Token', '5.0');

View File

@ -0,0 +1,44 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Behaviour.taggable
*
* @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Behaviour\Taggable\Extension\Taggable;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.2.0
*/
public function register(Container $container)
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new Taggable(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('behaviour', 'taggable')
);
return $plugin;
}
);
}
};

View File

@ -0,0 +1,343 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Behaviour.taggable
*
* @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\Plugin\Behaviour\Taggable\Extension;
use Joomla\CMS\Event\Model\BeforeBatchEvent;
use Joomla\CMS\Event\Table\AfterLoadEvent;
use Joomla\CMS\Event\Table\AfterResetEvent;
use Joomla\CMS\Event\Table\AfterStoreEvent;
use Joomla\CMS\Event\Table\BeforeDeleteEvent;
use Joomla\CMS\Event\Table\BeforeStoreEvent;
use Joomla\CMS\Event\Table\ObjectCreateEvent;
use Joomla\CMS\Event\Table\SetNewTagsEvent;
use Joomla\CMS\Helper\TagsHelper;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Table\TableInterface;
use Joomla\CMS\Tag\TaggableTableInterface;
use Joomla\Event\SubscriberInterface;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Implements the Taggable behaviour which allows extensions to automatically support tags for their content items.
*
* This plugin supersedes JHelperObserverTags.
*
* @since 4.0.0
*/
final class Taggable extends CMSPlugin implements SubscriberInterface
{
/**
* Returns an array of events this subscriber will listen to.
*
* @return array
*
* @since 4.2.0
*/
public static function getSubscribedEvents(): array
{
return [
'onTableObjectCreate' => 'onTableObjectCreate',
'onTableBeforeStore' => 'onTableBeforeStore',
'onTableAfterStore' => 'onTableAfterStore',
'onTableBeforeDelete' => 'onTableBeforeDelete',
'onTableSetNewTags' => 'onTableSetNewTags',
'onTableAfterReset' => 'onTableAfterReset',
'onTableAfterLoad' => 'onTableAfterLoad',
'onBeforeBatch' => 'onBeforeBatch',
];
}
/**
* Runs when a new table object is being created
*
* @param ObjectCreateEvent $event The event to handle
*
* @return void
*
* @since 4.0.0
*/
public function onTableObjectCreate(ObjectCreateEvent $event)
{
// Extract arguments
/** @var TableInterface $table */
$table = $event['subject'];
// If the tags table doesn't implement the interface bail
if (!($table instanceof TaggableTableInterface)) {
return;
}
// If the table already has a tags helper we have nothing to do
if (!\is_null($table->getTagsHelper())) {
return;
}
$tagsHelper = new TagsHelper();
$tagsHelper->typeAlias = $table->typeAlias;
$table->setTagsHelper($tagsHelper);
// This is required because getTagIds overrides the tags property of the Tags Helper.
$cloneHelper = clone $table->getTagsHelper();
$tagIds = $cloneHelper->getTagIds($table->getId(), $table->getTypeAlias());
if (!empty($tagIds)) {
$table->getTagsHelper()->tags = explode(',', $tagIds);
}
}
/**
* Pre-processor for $table->store($updateNulls)
*
* @param BeforeStoreEvent $event The event to handle
*
* @return void
*
* @since 4.0.0
*/
public function onTableBeforeStore(BeforeStoreEvent $event)
{
// Extract arguments
/** @var TableInterface $table */
$table = $event['subject'];
// If the tags table doesn't implement the interface bail
if (!($table instanceof TaggableTableInterface)) {
return;
}
// If the table doesn't have a tags helper we can't proceed
if (\is_null($table->getTagsHelper())) {
return;
}
/** @var TagsHelper $tagsHelper */
$tagsHelper = $table->getTagsHelper();
$tagsHelper->typeAlias = $table->getTypeAlias();
$newTags = $table->newTags ?? [];
if (empty($newTags)) {
$tagsHelper->preStoreProcess($table);
} else {
$tagsHelper->preStoreProcess($table, (array) $newTags);
}
}
/**
* Post-processor for $table->store($updateNulls)
*
* @param AfterStoreEvent $event The event to handle
*
* @return void
*
* @since 4.0.0
*/
public function onTableAfterStore(AfterStoreEvent $event)
{
// Extract arguments
/** @var TableInterface $table */
$table = $event['subject'];
$result = $event['result'];
if (!$result) {
return;
}
if (!\is_object($table) || !($table instanceof TaggableTableInterface)) {
return;
}
// If the table doesn't have a tags helper we can't proceed
if (\is_null($table->getTagsHelper())) {
return;
}
// Get the Tags helper and assign the parsed alias
/** @var TagsHelper $tagsHelper */
$tagsHelper = $table->getTagsHelper();
$tagsHelper->typeAlias = $table->getTypeAlias();
$newTags = $table->newTags ?? [];
if (empty($newTags)) {
$result = $tagsHelper->postStoreProcess($table);
} else {
if (\is_string($newTags) && (strpos($newTags, ',') !== false)) {
$newTags = explode(',', $newTags);
} elseif (!\is_array($newTags)) {
$newTags = (array) $newTags;
}
$result = $tagsHelper->postStoreProcess($table, $newTags);
}
}
/**
* Pre-processor for $table->delete($pk)
*
* @param BeforeDeleteEvent $event The event to handle
*
* @return void
*
* @since 4.0.0
*/
public function onTableBeforeDelete(BeforeDeleteEvent $event)
{
// Extract arguments
/** @var TableInterface $table */
$table = $event['subject'];
$pk = $event['pk'];
// If the tags table doesn't implement the interface bail
if (!($table instanceof TaggableTableInterface)) {
return;
}
// If the table doesn't have a tags helper we can't proceed
if (\is_null($table->getTagsHelper())) {
return;
}
// Get the Tags helper and assign the parsed alias
$table->getTagsHelper()->typeAlias = $table->getTypeAlias();
$table->getTagsHelper()->deleteTagData($table, $pk);
}
/**
* Handles the tag setting in $table->batchTag($value, $pks, $contexts)
*
* @param SetNewTagsEvent $event The event to handle
*
* @return void
*
* @since 4.0.0
*/
public function onTableSetNewTags(SetNewTagsEvent $event)
{
// Extract arguments
/** @var TableInterface $table */
$table = $event['subject'];
$newTags = $event['newTags'];
$replaceTags = $event['replaceTags'];
// If the tags table doesn't implement the interface bail
if (!($table instanceof TaggableTableInterface)) {
return;
}
// If the table doesn't have a tags helper we can't proceed
if (\is_null($table->getTagsHelper())) {
return;
}
// Get the Tags helper and assign the parsed alias
/** @var TagsHelper $tagsHelper */
$tagsHelper = $table->getTagsHelper();
$tagsHelper->typeAlias = $table->getTypeAlias();
if (!$tagsHelper->postStoreProcess($table, $newTags, $replaceTags)) {
throw new \RuntimeException($table->getError());
}
}
/**
* Runs when an existing table object is reset
*
* @param AfterResetEvent $event The event to handle
*
* @return void
*
* @since 4.0.0
*/
public function onTableAfterReset(AfterResetEvent $event)
{
// Extract arguments
/** @var TableInterface $table */
$table = $event['subject'];
// If the tags table doesn't implement the interface bail
if (!($table instanceof TaggableTableInterface)) {
return;
}
// Parse the type alias
$tagsHelper = new TagsHelper();
$tagsHelper->typeAlias = $table->getTypeAlias();
$table->setTagsHelper($tagsHelper);
}
/**
* Runs when an existing table object has been loaded
*
* @param AfterLoadEvent $event The event to handle
*
* @return void
*
* @since 4.0.0
*/
public function onTableAfterLoad(AfterLoadEvent $event)
{
// Extract arguments
/** @var TableInterface $table */
$table = $event['subject'];
// If the tags table doesn't implement the interface bail
if (!($table instanceof TaggableTableInterface)) {
return;
}
// If the table doesn't have a tags helper we can't proceed
if (\is_null($table->getTagsHelper())) {
return;
}
// This is required because getTagIds overrides the tags property of the Tags Helper.
$cloneHelper = clone $table->getTagsHelper();
$tagIds = $cloneHelper->getTagIds($table->getId(), $table->getTypeAlias());
if (!empty($tagIds)) {
$table->getTagsHelper()->tags = explode(',', $tagIds);
}
}
/**
* Runs when an existing table object has been loaded
*
* @param BeforeBatchEvent $event The event to handle
*
* @return void
*
* @since 4.0.0
*/
public function onBeforeBatch(BeforeBatchEvent $event)
{
/** @var TableInterface $sourceTable */
$sourceTable = $event['src'];
if (!($sourceTable instanceof TaggableTableInterface)) {
return;
}
if ($event['type'] === 'copy') {
$sourceTable->newTags = $sourceTable->getTagsHelper()->tags;
} else {
/**
* All other batch actions we don't want the tags to be modified so clear the helper - that way no actions
* will be performed on store
*/
$sourceTable->clearTagsHelper();
}
}
}

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="behaviour" method="upgrade">
<name>plg_behaviour_taggable</name>
<version>4.0.0</version>
<creationDate>2015-08</creationDate>
<author>Joomla! Project</author>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<copyright>(C) 2016 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<description>PLG_BEHAVIOUR_TAGGABLE_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\Behaviour\Taggable</namespace>
<files>
<folder plugin="taggable">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_behaviour_taggable.ini</language>
<language tag="en-GB">language/en-GB/plg_behaviour_taggable.sys.ini</language>
</languages>
<config />
</extension>

View File

@ -0,0 +1,50 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Behaviour.versionable
*
* @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Helper\CMSHelper;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Filter\InputFilter;
use Joomla\Plugin\Behaviour\Versionable\Extension\Versionable;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.2.0
*/
public function register(Container $container)
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new Versionable(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('behaviour', 'versionable'),
new InputFilter(),
new CMSHelper()
);
$plugin->setApplication(Factory::getApplication());
return $plugin;
}
);
}
};

View File

@ -0,0 +1,157 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Behaviour.versionable
*
* @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\Plugin\Behaviour\Versionable\Extension;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Event\Table\AfterStoreEvent;
use Joomla\CMS\Event\Table\BeforeDeleteEvent;
use Joomla\CMS\Helper\CMSHelper;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Versioning\VersionableTableInterface;
use Joomla\CMS\Versioning\Versioning;
use Joomla\Event\DispatcherInterface;
use Joomla\Event\SubscriberInterface;
use Joomla\Filter\InputFilter;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Implements the Versionable behaviour which allows extensions to automatically support content history for their content items.
*
* This plugin supersedes JTableObserverContenthistory.
*
* @since 4.0.0
*/
final class Versionable extends CMSPlugin implements SubscriberInterface
{
/**
* Returns an array of events this subscriber will listen to.
*
* @return array
*
* @since 4.2.0
*/
public static function getSubscribedEvents(): array
{
return [
'onTableAfterStore' => 'onTableAfterStore',
'onTableBeforeDelete' => 'onTableBeforeDelete',
];
}
/**
* The input filter
*
* @var InputFilter
* @since 4.2.0
*/
private $filter;
/**
* The CMS helper
*
* @var CMSHelper
* @since 4.2.0
*/
private $helper;
/**
* Constructor.
*
* @param DispatcherInterface $dispatcher The dispatcher
* @param array $config An optional associative array of configuration settings
* @param InputFilter $filter The input filter
* @param CMSHelper $helper The CMS helper
*
* @since 4.0.0
*/
public function __construct(DispatcherInterface $dispatcher, array $config, InputFilter $filter, CMSHelper $helper)
{
parent::__construct($dispatcher, $config);
$this->filter = $filter;
$this->helper = $helper;
}
/**
* Post-processor for $table->store($updateNulls)
*
* @param AfterStoreEvent $event The event to handle
*
* @return void
*
* @since 4.0.0
*/
public function onTableAfterStore(AfterStoreEvent $event)
{
// Extract arguments
/** @var VersionableTableInterface $table */
$table = $event['subject'];
$result = $event['result'];
if (!$result) {
return;
}
if (!(\is_object($table) && $table instanceof VersionableTableInterface)) {
return;
}
// Get the Tags helper and assign the parsed alias
$typeAlias = $table->getTypeAlias();
$aliasParts = explode('.', $typeAlias);
if ($aliasParts[0] === '' || !ComponentHelper::getParams($aliasParts[0])->get('save_history', 0)) {
return;
}
$id = $table->getId();
$data = $this->helper->getDataObject($table);
$input = $this->getApplication()->getInput();
$jform = $input->get('jform', [], 'array');
$versionNote = '';
if (isset($jform['version_note'])) {
$versionNote = $this->filter->clean($jform['version_note'], 'string');
}
Versioning::store($typeAlias, $id, $data, $versionNote);
}
/**
* Pre-processor for $table->delete($pk)
*
* @param BeforeDeleteEvent $event The event to handle
*
* @return void
*
* @since 4.0.0
*/
public function onTableBeforeDelete(BeforeDeleteEvent $event)
{
// Extract arguments
/** @var VersionableTableInterface $table */
$table = $event['subject'];
if (!(\is_object($table) && $table instanceof VersionableTableInterface)) {
return;
}
$typeAlias = $table->getTypeAlias();
$aliasParts = explode('.', $typeAlias);
if ($aliasParts[0] && ComponentHelper::getParams($aliasParts[0])->get('save_history', 0)) {
Versioning::delete($typeAlias, $table->getId());
}
}
}

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="behaviour" method="upgrade">
<name>plg_behaviour_versionable</name>
<version>4.0.0</version>
<creationDate>2015-08</creationDate>
<author>Joomla! Project</author>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<copyright>(C) 2016 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<description>PLG_BEHAVIOUR_VERSIONABLE_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\Behaviour\Versionable</namespace>
<files>
<folder plugin="versionable">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_behaviour_versionable.ini</language>
<language tag="en-GB">language/en-GB/plg_behaviour_versionable.sys.ini</language>
</languages>
<config />
</extension>

View File

@ -0,0 +1 @@
<!DOCTYPE html><title></title>

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="content" method="upgrade">
<name>plg_content_confirmconsent</name>
<author>Joomla! Project</author>
<creationDate>2018-05</creationDate>
<copyright>(C) 2018 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>3.9.0</version>
<description>PLG_CONTENT_CONFIRMCONSENT_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\Content\ConfirmConsent</namespace>
<files>
<folder plugin="confirmconsent">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_content_confirmconsent.ini</language>
<language tag="en-GB">language/en-GB/plg_content_confirmconsent.sys.ini</language>
</languages>
<config>
<fields name="params">
<fieldset name="basic" addfieldprefix="Joomla\Component\Content\Administrator\Field">
<field
name="consentbox_text"
type="textarea"
label="PLG_CONTENT_CONFIRMCONSENT_FIELD_NOTE_LABEL"
description="PLG_CONTENT_CONFIRMCONSENT_FIELD_NOTE_DESC"
hint="PLG_CONTENT_CONFIRMCONSENT_FIELD_NOTE_DEFAULT"
rows="7"
cols="20"
filter="html"
/>
<field
name="privacy_type"
type="list"
label="PLG_CONTENT_CONFIRMCONSENT_FIELD_TYPE_LABEL"
default="article"
validate="options"
>
<option value="article">PLG_CONTENT_CONFIRMCONSENT_FIELD_TYPE_ARTICLE</option>
<option value="menu_item">PLG_CONTENT_CONFIRMCONSENT_FIELD_TYPE_MENU_ITEM</option>
</field>
<field
name="privacy_article"
type="modal_article"
label="PLG_CONTENT_CONFIRMCONSENT_FIELD_ARTICLE_LABEL"
description="PLG_CONTENT_CONFIRMCONSENT_FIELD_ARTICLE_DESC"
select="true"
new="true"
edit="true"
clear="true"
filter="integer"
showon="privacy_type:article"
/>
<field
addfieldprefix="Joomla\Component\Menus\Administrator\Field"
name="privacy_menu_item"
type="modal_menu"
label="PLG_CONTENT_CONFIRMCONSENT_FIELD_MENU_ITEM_LABEL"
select="true"
new="true"
edit="true"
clear="true"
filter="integer"
showon="privacy_type:menu_item"
/>
</fieldset>
</fields>
</config>
</extension>

View File

@ -0,0 +1,46 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Content.confirmconsent
*
* @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Content\ConfirmConsent\Extension\ConfirmConsent;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.3.0
*/
public function register(Container $container)
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new ConfirmConsent(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('content', 'confirmconsent')
);
$plugin->setApplication(Factory::getApplication());
return $plugin;
}
);
}
};

View File

@ -0,0 +1,92 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Content.confirmconsent
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Plugin\Content\ConfirmConsent\Extension;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Plugin\CMSPlugin;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* The Joomla Core confirm consent plugin
*
* @since 3.9.0
*/
final class ConfirmConsent extends CMSPlugin
{
/**
* Load the language file on instantiation.
*
* @var boolean
*
* @since 3.9.0
*/
protected $autoloadLanguage = true;
/**
* The supported form contexts
*
* @var array
*
* @since 3.9.0
*/
protected $supportedContext = [
'com_contact.contact',
'com_privacy.request',
];
/**
* Add additional fields to the supported forms
*
* @param Form $form The form to be altered.
* @param mixed $data The associated data for the form.
*
* @return boolean
*
* @since 3.9.0
*/
public function onContentPrepareForm(Form $form, $data)
{
if ($this->getApplication()->isClient('administrator') || !\in_array($form->getName(), $this->supportedContext)) {
return true;
}
// Get the consent box Text & the selected privacyarticle
$consentboxText = (string) $this->params->get(
'consentbox_text',
$this->getApplication()->getLanguage()->_('PLG_CONTENT_CONFIRMCONSENT_FIELD_NOTE_DEFAULT')
);
$privacyArticle = $this->params->get('privacy_article', false);
$privacyType = $this->params->get('privacy_type', 'article');
$privacyMenuItem = $this->params->get('privacy_menu_item', false);
$form->load('
<form>
<fieldset name="default" addfieldprefix="Joomla\\Plugin\\Content\\ConfirmConsent\\Field">
<field
name="consentbox"
type="ConsentBox"
articleid="' . $privacyArticle . '"
menu_item_id="' . $privacyMenuItem . '"
privacy_type="' . $privacyType . '"
label="PLG_CONTENT_CONFIRMCONSENT_CONSENTBOX_LABEL"
required="true"
>
<option value="0">' . htmlspecialchars($consentboxText, ENT_COMPAT, 'UTF-8') . '</option>
</field>
</fieldset>
</form>');
return true;
}
}

View File

@ -0,0 +1,339 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Content.confirmconsent
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Plugin\Content\ConfirmConsent\Field;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Field\CheckboxesField;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Associations;
use Joomla\CMS\Language\Multilanguage;
use Joomla\CMS\Router\Route;
use Joomla\Component\Content\Site\Helper\RouteHelper;
use Joomla\Database\Exception\ExecutionFailureException;
use Joomla\Database\ParameterType;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Consentbox Field class for the Confirm Consent Plugin.
*
* @since 3.9.1
*/
class ConsentBoxField extends CheckboxesField
{
/**
* The form field type.
*
* @var string
* @since 3.9.1
*/
protected $type = 'ConsentBox';
/**
* Flag to tell the field to always be in multiple values mode.
*
* @var boolean
* @since 3.9.1
*/
protected $forceMultiple = false;
/**
* The article ID.
*
* @var integer
* @since 3.9.1
*/
protected $articleid;
/**
* The menu item ID.
*
* @var integer
* @since 4.0.0
*/
protected $menuItemId;
/**
* Type of the privacy policy.
*
* @var string
* @since 4.0.0
*/
protected $privacyType;
/**
* Method to set certain otherwise inaccessible properties of the form field object.
*
* @param string $name The property name for which to set the value.
* @param mixed $value The value of the property.
*
* @return void
*
* @since 3.9.1
*/
public function __set($name, $value)
{
switch ($name) {
case 'articleid':
$this->articleid = (int) $value;
break;
default:
parent::__set($name, $value);
}
}
/**
* Method to get certain otherwise inaccessible properties from the form field object.
*
* @param string $name The property name for which to get the value.
*
* @return mixed The property value or null.
*
* @since 3.9.1
*/
public function __get($name)
{
if ($name == 'articleid') {
return $this->$name;
}
return parent::__get($name);
}
/**
* Method to attach a JForm object to the field.
*
* @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object.
* @param mixed $value The form field value to validate.
* @param string $group The field name group control value. This acts as an array container for the field.
* For example if the field has name="foo" and the group value is set to "bar" then the
* full field name would end up being "bar[foo]".
*
* @return boolean True on success.
*
* @see \Joomla\CMS\Form\FormField::setup()
* @since 3.9.1
*/
public function setup(\SimpleXMLElement $element, $value, $group = null)
{
$return = parent::setup($element, $value, $group);
if ($return) {
$this->articleid = (int) $this->element['articleid'];
$this->menuItemId = (int) $this->element['menu_item_id'];
$this->privacyType = (string) $this->element['privacy_type'];
}
return $return;
}
/**
* Method to get the field label markup.
*
* @return string The field label markup.
*
* @since 3.9.1
*/
protected function getLabel()
{
if ($this->hidden) {
return '';
}
$data = $this->getLayoutData();
// Forcing the Alias field to display the tip below
$position = $this->element['name'] == 'alias' ? ' data-bs-placement="bottom" ' : '';
// When we have an article let's add the modal and make the title clickable
$hasLink = ($data['privacyType'] === 'article' && $data['articleid'])
|| ($data['privacyType'] === 'menu_item' && $data['menuItemId']);
if ($hasLink) {
$attribs['data-bs-toggle'] = 'modal';
$data['label'] = HTMLHelper::_(
'link',
'#modal-' . $this->id,
$data['label'],
$attribs
);
}
// Here mainly for B/C with old layouts. This can be done in the layouts directly
$extraData = [
'text' => $data['label'],
'for' => $this->id,
'classes' => explode(' ', $data['labelclass']),
'position' => $position,
];
return $this->getRenderer($this->renderLabelLayout)->render(array_merge($data, $extraData));
}
/**
* Method to get the field input markup.
*
* @return string The field input markup.
*
* @since 4.0.0
*/
protected function getInput()
{
$modalHtml = '';
$layoutData = $this->getLayoutData();
$hasLink = ($this->privacyType === 'article' && $this->articleid)
|| ($this->privacyType === 'menu_item' && $this->menuItemId);
if ($hasLink) {
$modalParams['title'] = $layoutData['label'];
$modalParams['url'] = ($this->privacyType === 'menu_item') ? $this->getAssignedMenuItemUrl() : $this->getAssignedArticleUrl();
$modalParams['height'] = '100%';
$modalParams['width'] = '100%';
$modalParams['bodyHeight'] = 70;
$modalParams['modalWidth'] = 80;
$modalHtml = HTMLHelper::_('bootstrap.renderModal', 'modal-' . $this->id, $modalParams);
}
return $modalHtml . parent::getInput();
}
/**
* Method to get the data to be passed to the layout for rendering.
*
* @return array
*
* @since 3.9.1
*/
protected function getLayoutData()
{
$data = parent::getLayoutData();
$extraData = [
'articleid' => (int) $this->articleid,
'menuItemId' => (int) $this->menuItemId,
'privacyType' => (string) $this->privacyType,
];
return array_merge($data, $extraData);
}
/**
* Return the url of the assigned article based on the current user language
*
* @return string Returns the link to the article
*
* @since 3.9.1
*/
private function getAssignedArticleUrl()
{
$db = $this->getDatabase();
// Get the info from the article
$query = $db->getQuery(true)
->select($db->quoteName(['id', 'catid', 'language']))
->from($db->quoteName('#__content'))
->where($db->quoteName('id') . ' = ' . (int) $this->articleid);
$db->setQuery($query);
try {
$article = $db->loadObject();
} catch (ExecutionFailureException $e) {
// Something at the database layer went wrong
return Route::_(
'index.php?option=com_content&view=article&id='
. $this->articleid . '&tmpl=component'
);
}
if (!\is_object($article)) {
// We have not found the article object lets show a 404 to the user
return Route::_(
'index.php?option=com_content&view=article&id='
. $this->articleid . '&tmpl=component'
);
}
if (!Associations::isEnabled()) {
return Route::_(
RouteHelper::getArticleRoute(
$article->id,
$article->catid,
$article->language
) . '&tmpl=component'
);
}
$associatedArticles = Associations::getAssociations('com_content', '#__content', 'com_content.item', $article->id);
$currentLang = Factory::getLanguage()->getTag();
if (isset($associatedArticles) && $currentLang !== $article->language && \array_key_exists($currentLang, $associatedArticles)) {
return Route::_(
RouteHelper::getArticleRoute(
$associatedArticles[$currentLang]->id,
$associatedArticles[$currentLang]->catid,
$associatedArticles[$currentLang]->language
) . '&tmpl=component'
);
}
// Association is enabled but this article is not associated
return Route::_(
'index.php?option=com_content&view=article&id='
. $article->id . '&catid=' . $article->catid
. '&tmpl=component&lang=' . $article->language
);
}
/**
* Get privacy menu item URL. If the site is a multilingual website and there is associated menu item for the
* current language, the URL of the associated menu item will be returned.
*
* @return string
*
* @since 4.0.0
*/
private function getAssignedMenuItemUrl()
{
$itemId = $this->menuItemId;
$languageSuffix = '';
if ($itemId > 0 && Associations::isEnabled()) {
$privacyAssociated = Associations::getAssociations('com_menus', '#__menu', 'com_menus.item', $itemId, 'id', '', '');
$currentLang = Factory::getLanguage()->getTag();
if (isset($privacyAssociated[$currentLang])) {
$itemId = $privacyAssociated[$currentLang]->id;
}
if (Multilanguage::isEnabled()) {
$db = $this->getDatabase();
$query = $db->getQuery(true)
->select($db->quoteName(['id', 'language']))
->from($db->quoteName('#__menu'))
->where($db->quoteName('id') . ' = :id')
->bind(':id', $itemId, ParameterType::INTEGER);
$db->setQuery($query);
$menuItem = $db->loadObject();
$languageSuffix = '&lang=' . $menuItem->language;
}
}
return Route::_(
'index.php?Itemid=' . (int) $itemId . '&tmpl=component' . $languageSuffix
);
}
}

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="content" method="upgrade">
<name>plg_content_contact</name>
<author>Joomla! Project</author>
<creationDate>2014-01</creationDate>
<copyright>(C) 2014 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>3.2.2</version>
<description>PLG_CONTENT_CONTACT_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\Content\Contact</namespace>
<files>
<folder plugin="contact">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_content_contact.ini</language>
<language tag="en-GB">language/en-GB/plg_content_contact.sys.ini</language>
</languages>
<config>
<fields name="params">
<fieldset name="basic">
<field
name="url"
type="list"
label="PLG_CONTENT_CONTACT_PARAM_URL_LABEL"
description="PLG_CONTENT_CONTACT_PARAM_URL_DESCRIPTION"
default="url"
validate="options"
>
<option value="url">PLG_CONTENT_CONTACT_PARAM_URL_URL</option>
<option value="webpage">PLG_CONTENT_CONTACT_PARAM_URL_WEBPAGE</option>
<option value="email">PLG_CONTENT_CONTACT_PARAM_URL_EMAIL</option>
</field>
<field
name="link_to_alias"
type="radio"
label="PLG_CONTENT_CONTACT_PARAM_ALIAS_LABEL"
description="PLG_CONTENT_CONTACT_PARAM_ALIAS_DESCRIPTION"
default="0"
layout="joomla.form.field.radio.switcher"
filter="integer"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
</fieldset>
</fields>
</config>
</extension>

View File

@ -0,0 +1,48 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Content.contact
*
* @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\Database\DatabaseInterface;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Content\Contact\Extension\Contact;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.3.0
*/
public function register(Container $container)
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new Contact(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('content', 'contact')
);
$plugin->setApplication(Factory::getApplication());
$plugin->setDatabase($container->get(DatabaseInterface::class));
return $plugin;
}
);
}
};

View File

@ -0,0 +1,146 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Content.contact
*
* @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Plugin\Content\Contact\Extension;
use Joomla\CMS\Language\Multilanguage;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Router\Route;
use Joomla\Component\Contact\Site\Helper\RouteHelper;
use Joomla\Database\DatabaseAwareTrait;
use Joomla\Database\ParameterType;
use Joomla\Registry\Registry;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Contact Plugin
*
* @since 3.2
*/
final class Contact extends CMSPlugin
{
use DatabaseAwareTrait;
/**
* Plugin that retrieves contact information for contact
*
* @param string $context The context of the content being passed to the plugin.
* @param mixed &$row An object with a "text" property
* @param mixed $params Additional parameters. See {@see PlgContentContent()}.
* @param integer $page Optional page number. Unused. Defaults to zero.
*
* @return void
*/
public function onContentPrepare($context, &$row, $params, $page = 0)
{
$allowed_contexts = ['com_content.category', 'com_content.article', 'com_content.featured'];
if (!\in_array($context, $allowed_contexts)) {
return;
}
// Return if we don't have valid params or don't link the author
if (!($params instanceof Registry) || !$params->get('link_author')) {
return;
}
// Return if an alias is used
if ((int) $this->params->get('link_to_alias', 0) === 0 && $row->created_by_alias != '') {
return;
}
// Return if we don't have a valid article id
if (!isset($row->id) || !(int) $row->id) {
return;
}
$contact = $this->getContactData($row->created_by);
if ($contact === null) {
return;
}
$row->contactid = $contact->contactid;
$row->webpage = $contact->webpage;
$row->email = $contact->email_to;
$url = $this->params->get('url', 'url');
if ($row->contactid && $url === 'url') {
$row->contact_link = Route::_(RouteHelper::getContactRoute($contact->contactid . ':' . $contact->alias, $contact->catid));
} elseif ($row->webpage && $url === 'webpage') {
$row->contact_link = $row->webpage;
} elseif ($row->email && $url === 'email') {
$row->contact_link = 'mailto:' . $row->email;
} else {
$row->contact_link = '';
}
}
/**
* Retrieve Contact
*
* @param int $userId Id of the user who created the article
*
* @return stdClass|null Object containing contact details or null if not found
*/
private function getContactData($userId)
{
static $contacts = [];
// Note: don't use isset() because value could be null.
if (\array_key_exists($userId, $contacts)) {
return $contacts[$userId];
}
$db = $this->getDatabase();
$query = $db->getQuery(true);
$userId = (int) $userId;
$query->select($db->quoteName('contact.id', 'contactid'))
->select(
$db->quoteName(
[
'contact.alias',
'contact.catid',
'contact.webpage',
'contact.email_to',
]
)
)
->from($db->quoteName('#__contact_details', 'contact'))
->where(
[
$db->quoteName('contact.published') . ' = 1',
$db->quoteName('contact.user_id') . ' = :createdby',
]
)
->bind(':createdby', $userId, ParameterType::INTEGER);
if (Multilanguage::isEnabled() === true) {
$query->where(
'(' . $db->quoteName('contact.language') . ' IN ('
. implode(',', $query->bindArray([$this->getApplication()->getLanguage()->getTag(), '*'], ParameterType::STRING))
. ') OR ' . $db->quoteName('contact.language') . ' IS NULL)'
);
}
$query->order($db->quoteName('contact.id') . ' DESC')
->setLimit(1);
$db->setQuery($query);
$contacts[$userId] = $db->loadObject();
return $contacts[$userId];
}
}

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="content" method="upgrade">
<name>plg_content_emailcloak</name>
<author>Joomla! Project</author>
<creationDate>2005-11</creationDate>
<copyright>(C) 2005 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>3.0.0</version>
<description>PLG_CONTENT_EMAILCLOAK_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\Content\EmailCloak</namespace>
<files>
<folder plugin="emailcloak">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_content_emailcloak.ini</language>
<language tag="en-GB">language/en-GB/plg_content_emailcloak.sys.ini</language>
</languages>
<config>
<fields name="params">
<fieldset name="basic">
<field
name="mode"
type="list"
label="PLG_CONTENT_EMAILCLOAK_MODE_LABEL"
default="1"
filter="integer"
validate="options"
>
<option value="0">PLG_CONTENT_EMAILCLOAK_NONLINKABLE</option>
<option value="1">PLG_CONTENT_EMAILCLOAK_LINKABLE</option>
</field>
</fieldset>
</fields>
</config>
</extension>

View File

@ -0,0 +1,46 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Content.emailcloak
*
* @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Content\EmailCloak\Extension\EmailCloak;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.3.0
*/
public function register(Container $container)
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new EmailCloak(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('content', 'emailcloak')
);
$plugin->setApplication(Factory::getApplication());
return $plugin;
}
);
}
};

View File

@ -0,0 +1,494 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Content.emailcloak
*
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Plugin\Content\EmailCloak\Extension;
use Joomla\CMS\Event\Content\ContentPrepareEvent;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\SubscriberInterface;
use Joomla\String\StringHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Email cloak plugin class.
*
* @since 1.5
*/
final class EmailCloak extends CMSPlugin implements SubscriberInterface
{
/**
* Returns an array of events this subscriber will listen to.
*
* @return array
*
* @since 5.0.0
*/
public static function getSubscribedEvents(): array
{
return ['onContentPrepare' => 'onContentPrepare'];
}
/**
* Plugin that cloaks all emails in content from spambots via Javascript.
*
* @param ContentPrepareEvent $event Event instance
*
* @return void
*/
public function onContentPrepare(ContentPrepareEvent $event)
{
// Don't run if in the API Application
// Don't run this plugin when the content is being indexed
if ($this->getApplication()->isClient('api') || $event->getContext() === 'com_finder.indexer') {
return;
}
// Get content item
$item = $event->getItem();
// If the item does not have a text property there is nothing to do
if (!property_exists($item, 'text')) {
return;
}
$text = $this->cloak($item->text);
if ($text) {
$item->text = $text;
}
}
/**
* Generate a search pattern based on link and text.
*
* @param string $link The target of an email link.
* @param string $text The text enclosed by the link.
*
* @return string A regular expression that matches a link containing the parameters.
*/
private function getPattern($link, $text)
{
$pattern = '~(?:<a ([^>]*)href\s*=\s*"mailto:' . $link . '"([^>]*))>' . $text . '</a>~i';
return $pattern;
}
/**
* Cloak all emails in text from spambots via Javascript.
*
* @param string $text The string to be cloaked.
*
* @return string
*/
private function cloak($text)
{
/*
* Check for presence of {emailcloak=off} which is explicits disables this
* bot for the item.
*/
if (StringHelper::strpos($text, '{emailcloak=off}') !== false) {
return StringHelper::str_ireplace('{emailcloak=off}', '', $text);
}
// Simple performance check to determine whether bot should process further.
if (StringHelper::strpos($text, '@') === false) {
return '';
}
$mode = (int) $this->params->def('mode', 1);
$mode = $mode === 1;
// Example: any@example.org
$searchEmail = '([\w\.\'\-\+]+\@(?:[a-z0-9\.\-]+\.)+(?:[a-zA-Z0-9\-]{2,24}))';
// Example: any@example.org?subject=anyText
$searchEmailLink = $searchEmail . '([?&][\x20-\x7f][^"<>]+)';
// Any Text
$searchText = '((?:[\x20-\x7f]|[\xA1-\xFF]|[\xC2-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF4][\x80-\xBF]{3})[^<>]+)';
// Any Image link
$searchImage = '(<img[^>]+>)';
// Any Text with <span or <strong
$searchTextSpan = '(<span[^>]+>|<span>|<strong>|<strong><span[^>]+>|<strong><span>)' . $searchText . '(</span>|</strong>|</span></strong>)';
// Any address with <span or <strong
$searchEmailSpan = '(<span[^>]+>|<span>|<strong>|<strong><span[^>]+>|<strong><span>)' . $searchEmail . '(</span>|</strong>|</span></strong>)';
/*
* Search and fix derivatives of link code <a href="http://mce_host/ourdirectory/email@example.org"
* >email@example.org</a>. This happens when inserting an email in TinyMCE, cancelling its suggestion to add
* the mailto: prefix...
*/
$pattern = $this->getPattern($searchEmail, $searchEmail);
$pattern = str_replace('"mailto:', '"([\x20-\x7f][^<>]+/)', $pattern);
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
$mail = $regs[3][0];
$mailText = $regs[5][0];
$attribsBefore = $regs[1][0];
$attribsAfter = $regs[4][0];
// Check to see if mail text is different from mail addy
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 1, $attribsBefore, $attribsAfter);
// Replace the found address with the js cloaked email
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
}
/*
* Search and fix derivatives of link code <a href="http://mce_host/ourdirectory/email@example.org"
* >anytext</a>. This happens when inserting an email in TinyMCE, cancelling its suggestion to add
* the mailto: prefix...
*/
$pattern = $this->getPattern($searchEmail, $searchText);
$pattern = str_replace('"mailto:', '"([\x20-\x7f][^<>]+/)', $pattern);
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
$mail = $regs[3][0];
$mailText = $regs[5][0];
$attribsBefore = $regs[1][0];
$attribsAfter = $regs[4][0];
// Check to see if mail text is different from mail addy
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 0, $attribsBefore, $attribsAfter);
// Replace the found address with the js cloaked email
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
}
/*
* Search for derivatives of link code <a href="mailto:email@example.org"
* >email@example.org</a>
*/
$pattern = $this->getPattern($searchEmail, $searchEmail);
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
$mail = $regs[2][0];
$mailText = $regs[4][0];
$attribsBefore = $regs[1][0];
$attribsAfter = $regs[3][0];
// Check to see if mail text is different from mail addy
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 1, $attribsBefore, $attribsAfter);
// Replace the found address with the js cloaked email
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
}
/*
* Search for derivatives of link code <a href="mailto:email@amail.com"
* ><anyspan >email@amail.com</anyspan></a>
*/
$pattern = $this->getPattern($searchEmail, $searchEmailSpan);
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
$mail = $regs[2][0];
$mailText = $regs[4][0] . $regs[5][0] . $regs[6][0];
$attribsBefore = $regs[1][0];
$attribsAfter = $regs[3][0];
// Check to see if mail text is different from mail addy
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 1, $attribsBefore, $attribsAfter);
// Replace the found address with the js cloaked email
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
}
/*
* Search for derivatives of link code <a href="mailto:email@amail.com">
* <anyspan >anytext</anyspan></a>
*/
$pattern = $this->getPattern($searchEmail, $searchTextSpan);
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
$mail = $regs[2][0];
$mailText = $regs[4][0] . $regs[5][0] . $regs[6][0];
$attribsBefore = $regs[1][0];
$attribsAfter = $regs[3][0];
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 0, $attribsBefore, $attribsAfter);
// Replace the found address with the js cloaked email
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
}
/*
* Search for derivatives of link code <a href="mailto:email@example.org">
* anytext</a>
*/
$pattern = $this->getPattern($searchEmail, $searchText);
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
$mail = $regs[2][0];
$mailText = $regs[4][0];
$attribsBefore = $regs[1][0];
$attribsAfter = $regs[3][0];
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 0, $attribsBefore, $attribsAfter);
// Replace the found address with the js cloaked email
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
}
/*
* Search for derivatives of link code <a href="mailto:email@example.org">
* <img anything></a>
*/
$pattern = $this->getPattern($searchEmail, $searchImage);
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
$mail = $regs[2][0];
$mailText = $regs[4][0];
$attribsBefore = $regs[1][0];
$attribsAfter = $regs[3][0];
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 0, $attribsBefore, $attribsAfter);
// Replace the found address with the js cloaked email
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
}
/*
* Search for derivatives of link code <a href="mailto:email@example.org">
* <img anything>email@example.org</a>
*/
$pattern = $this->getPattern($searchEmail, $searchImage . $searchEmail);
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
$mail = $regs[2][0];
$mailText = $regs[4][0] . $regs[5][0];
$attribsBefore = $regs[1][0];
$attribsAfter = $regs[3][0];
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 1, $attribsBefore, $attribsAfter);
// Replace the found address with the js cloaked email
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
}
/*
* Search for derivatives of link code <a href="mailto:email@example.org">
* <img anything>any text</a>
*/
$pattern = $this->getPattern($searchEmail, $searchImage . $searchText);
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
$mail = $regs[2][0];
$mailText = $regs[4][0] . $regs[5][0];
$attribsBefore = $regs[1][0];
$attribsAfter = $regs[3][0];
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 0, $attribsBefore, $attribsAfter);
// Replace the found address with the js cloaked email
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
}
/*
* Search for derivatives of link code <a href="mailto:email@example.org?
* subject=Text">email@example.org</a>
*/
$pattern = $this->getPattern($searchEmailLink, $searchEmail);
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
$mail = $regs[2][0] . $regs[3][0];
$mailText = $regs[5][0];
$attribsBefore = $regs[1][0];
$attribsAfter = $regs[4][0];
// Needed for handling of Body parameter
$mail = str_replace('&amp;', '&', $mail);
// Check to see if mail text is different from mail addy
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 1, $attribsBefore, $attribsAfter);
// Replace the found address with the js cloaked email
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
}
/*
* Search for derivatives of link code <a href="mailto:email@example.org?
* subject=Text">anytext</a>
*/
$pattern = $this->getPattern($searchEmailLink, $searchText);
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
$mail = $regs[2][0] . $regs[3][0];
$mailText = $regs[5][0];
$attribsBefore = $regs[1][0];
$attribsAfter = $regs[4][0];
// Needed for handling of Body parameter
$mail = str_replace('&amp;', '&', $mail);
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 0, $attribsBefore, $attribsAfter);
// Replace the found address with the js cloaked email
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
}
/*
* Search for derivatives of link code <a href="mailto:email@amail.com?subject= Text"
* ><anyspan >email@amail.com</anyspan></a>
*/
$pattern = $this->getPattern($searchEmailLink, $searchEmailSpan);
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
$mail = $regs[2][0] . $regs[3][0];
$mailText = $regs[5][0] . $regs[6][0] . $regs[7][0];
$attribsBefore = $regs[1][0];
$attribsAfter = $regs[4][0];
// Check to see if mail text is different from mail addy
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 1, $attribsBefore, $attribsAfter);
// Replace the found address with the js cloaked email
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
}
/*
* Search for derivatives of link code <a href="mailto:email@amail.com?subject= Text">
* <anyspan >anytext</anyspan></a>
*/
$pattern = $this->getPattern($searchEmailLink, $searchTextSpan);
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
$mail = $regs[2][0] . $regs[3][0];
$mailText = $regs[5][0] . $regs[6][0] . $regs[7][0];
$attribsBefore = $regs[1][0];
$attribsAfter = $regs[4][0];
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 0, $attribsBefore, $attribsAfter);
// Replace the found address with the js cloaked email
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
}
/*
* Search for derivatives of link code
* <a href="mailto:email@amail.com?subject=Text"><img anything></a>
*/
$pattern = $this->getPattern($searchEmailLink, $searchImage);
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
$mail = $regs[2][0] . $regs[3][0];
$mailText = $regs[5][0];
$attribsBefore = $regs[1][0];
$attribsAfter = $regs[4][0];
// Needed for handling of Body parameter
$mail = str_replace('&amp;', '&', $mail);
// Check to see if mail text is different from mail addy
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 0, $attribsBefore, $attribsAfter);
// Replace the found address with the js cloaked email
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
}
/*
* Search for derivatives of link code
* <a href="mailto:email@amail.com?subject=Text"><img anything>email@amail.com</a>
*/
$pattern = $this->getPattern($searchEmailLink, $searchImage . $searchEmail);
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
$mail = $regs[2][0] . $regs[3][0];
$mailText = $regs[5][0] . $regs[6][0];
$attribsBefore = $regs[1][0];
$attribsAfter = $regs[4][0];
// Needed for handling of Body parameter
$mail = str_replace('&amp;', '&', $mail);
// Check to see if mail text is different from mail addy
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 1, $attribsBefore, $attribsAfter);
// Replace the found address with the js cloaked email
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
}
/*
* Search for derivatives of link code
* <a href="mailto:email@amail.com?subject=Text"><img anything>any text</a>
*/
$pattern = $this->getPattern($searchEmailLink, $searchImage . $searchText);
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
$mail = $regs[2][0] . $regs[3][0];
$mailText = $regs[5][0] . $regs[6][0];
$attribsBefore = $regs[1][0];
$attribsAfter = $regs[4][0];
// Needed for handling of Body parameter
$mail = str_replace('&amp;', '&', $mail);
// Check to see if mail text is different from mail addy
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mailText, 0, $attribsBefore, $attribsAfter);
// Replace the found address with the js cloaked email
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($regs[0][0]));
}
/*
* Search for plain text email addresses, such as email@example.org but within HTML tags:
* <img src="..." title="email@example.org"> or <input type="text" placeholder="email@example.org">
* The '<[^<]*>(*SKIP)(*F)|' trick is used to exclude this kind of occurrences
*/
$pattern = '~<[^<]*(?<!\/)>(*SKIP)(*F)|<[^>]+?(\w*=\"' . $searchEmail . '\")[^>]*\/>~i';
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
$mail = $regs[0][0];
$replacement = HTMLHelper::_('email.cloak', $mail, 0, $mail);
// Replace the found address with the js cloaked email
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($mail));
}
/*
* Search for plain text email addresses, such as email@example.org but within HTML attributes:
* <a title="email@example.org" href="#">email</a> or <li title="email@example.org">email</li>
*/
$pattern = '(<[^>]+?(\w*=\"' . $searchEmail . '")[^>]*>[^<]+<[^<]+>)';
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
$mail = $regs[0][0];
$replacement = HTMLHelper::_('email.cloak', $mail, 0, $mail);
// Replace the found address with the js cloaked email
$text = substr_replace($text, $replacement, $regs[0][1], \strlen($mail));
}
/*
* Search for plain text email addresses, such as email@example.org but not within HTML tags:
* <p>email@example.org</p>
* The '<[^<]*>(*SKIP)(*F)|' trick is used to exclude this kind of occurrences
* The '<[^<]*(?<!\/(?:src))>(*SKIP)(*F)|' exclude image files with @ in filename
*/
$pattern = '~<[^<]*(?<!\/(?:src))>(*SKIP)(*F)|' . $searchEmail . '~i';
while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) {
$mail = $regs[1][0];
$replacement = HTMLHelper::_('email.cloak', $mail, $mode, $mail);
// Replace the found address with the js cloaked email
$text = substr_replace($text, $replacement, $regs[1][1], \strlen($mail));
}
return $text;
}
}

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="content" method="upgrade">
<name>plg_content_fields</name>
<author>Joomla! Project</author>
<creationDate>2017-02</creationDate>
<copyright>(C) 2017 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>3.7.0</version>
<description>PLG_CONTENT_FIELDS_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\Content\Fields</namespace>
<files>
<folder plugin="fields">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_content_fields.ini</language>
<language tag="en-GB">language/en-GB/plg_content_fields.sys.ini</language>
</languages>
<config>
<fields name="params">
<fieldset name="basic">
</fieldset>
</fields>
</config>
</extension>

View File

@ -0,0 +1,44 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Content.fields
*
* @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Content\Fields\Extension\Fields;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.3.0
*/
public function register(Container $container)
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new Fields(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('content', 'fields')
);
return $plugin;
}
);
}
};

View File

@ -0,0 +1,155 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Content.fields
*
* @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Plugin\Content\Fields\Extension;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Component\Fields\Administrator\Helper\FieldsHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Plug-in to show a custom field in eg an article
* This uses the {fields ID} syntax
*
* @since 3.7.0
*/
final class Fields extends CMSPlugin
{
/**
* Plugin that shows a custom field
*
* @param string $context The context of the content being passed to the plugin.
* @param object &$item The item object. Note $article->text is also available
* @param object &$params The article params
* @param int $page The 'page' number
*
* @return void
*
* @since 3.7.0
*/
public function onContentPrepare($context, &$item, &$params, $page = 0)
{
// If the item has a context, overwrite the existing one
if ($context === 'com_finder.indexer' && !empty($item->context)) {
$context = $item->context;
} elseif ($context === 'com_finder.indexer') {
// Don't run this plugin when the content is being indexed and we have no real context
return;
}
// This plugin only works if $item is an object
if (!\is_object($item)) {
return;
}
// Don't run if there is no text property (in case of bad calls) or it is empty
if (!property_exists($item, 'text') || empty($item->text)) {
return;
}
// Prepare the text
if (property_exists($item, 'text') && strpos($item->text, 'field') !== false) {
$item->text = $this->prepare($item->text, $context, $item);
}
// Prepare the intro text
if (property_exists($item, 'introtext') && \is_string($item->introtext) && strpos($item->introtext, 'field') !== false) {
$item->introtext = $this->prepare($item->introtext, $context, $item);
}
}
/**
* Prepares the given string by parsing {field} and {fieldgroup} groups and replacing them.
*
* @param string $string The text to prepare
* @param string $context The context of the content
* @param object $item The item object
*
* @return string
*
* @since 3.8.1
*/
private function prepare($string, $context, $item)
{
// Search for {field ID} or {fieldgroup ID} tags and put the results into $matches.
$regex = '/{(field|fieldgroup)\s+(.*?)}/i';
preg_match_all($regex, $string, $matches, PREG_SET_ORDER);
if (!$matches) {
return $string;
}
$parts = FieldsHelper::extract($context);
if (!$parts || \count($parts) < 2) {
return $string;
}
$context = $parts[0] . '.' . $parts[1];
$fields = FieldsHelper::getFields($context, $item, true);
$fieldsById = [];
$groups = [];
// Rearranging fields in arrays for easier lookup later.
foreach ($fields as $field) {
$fieldsById[$field->id] = $field;
$groups[$field->group_id][] = $field;
}
foreach ($matches as $i => $match) {
// $match[0] is the full pattern match, $match[1] is the type (field or fieldgroup) and $match[2] the ID and optional the layout
$explode = explode(',', $match[2]);
$id = (int) $explode[0];
$output = '';
if ($match[1] === 'field' && $id) {
if (isset($fieldsById[$id])) {
$layout = !empty($explode[1]) ? trim($explode[1]) : $fieldsById[$id]->params->get('layout', 'render');
$output = FieldsHelper::render(
$context,
'field.' . $layout,
[
'item' => $item,
'context' => $context,
'field' => $fieldsById[$id],
]
);
}
} else {
if ($match[2] === '*') {
$match[0] = str_replace('*', '\*', $match[0]);
$renderFields = $fields;
} else {
$renderFields = $groups[$id] ?? '';
}
if ($renderFields) {
$layout = !empty($explode[1]) ? trim($explode[1]) : 'render';
$output = FieldsHelper::render(
$context,
'fields.' . $layout,
[
'item' => $item,
'context' => $context,
'fields' => $renderFields,
]
);
}
}
$string = preg_replace("|$match[0]|", addcslashes($output, '\\$'), $string, 1);
}
return $string;
}
}

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="content" method="upgrade">
<name>plg_content_finder</name>
<author>Joomla! Project</author>
<creationDate>2011-12</creationDate>
<copyright>(C) 2011 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>3.0.0</version>
<description>PLG_CONTENT_FINDER_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\Content\Finder</namespace>
<files>
<folder plugin="finder">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_content_finder.ini</language>
<language tag="en-GB">language/en-GB/plg_content_finder.sys.ini</language>
</languages>
<config>
<fields name="params">
</fields>
</config>
</extension>

View File

@ -0,0 +1,46 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Content.finder
*
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Content\Finder\Extension\Finder;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.4.0
*/
public function register(Container $container): void
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new Finder(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('content', 'finder')
);
$plugin->setApplication(Factory::getApplication());
return $plugin;
}
);
}
};

View File

@ -0,0 +1,176 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Content.finder
*
* @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Plugin\Content\Finder\Extension;
use Joomla\CMS\Event\Finder as FinderEvent;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Plugin\PluginHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Smart Search Content Plugin
*
* @since 2.5
*/
final class Finder extends CMSPlugin
{
/**
* Flag to check whether finder plugins already imported.
*
* @var bool
*
* @since 5.0.0
*/
protected $pluginsImported = false;
/**
* Smart Search after save content method.
* Content is passed by reference, but after the save, so no changes will be saved.
* Method is called right after the content is saved.
*
* @param string $context The context of the content passed to the plugin (added in 1.6)
* @param object $article A \Joomla\CMS\Table\Table\ object
* @param bool $isNew If the content has just been created
*
* @return void
*
* @since 2.5
*/
public function onContentAfterSave($context, $article, $isNew): void
{
$this->importFinderPlugins();
// Trigger the onFinderAfterSave event.
$this->getDispatcher()->dispatch('onFinderAfterSave', new FinderEvent\AfterSaveEvent('onFinderAfterSave', [
'context' => $context,
'subject' => $article,
'isNew' => $isNew,
]));
}
/**
* Smart Search before save content method.
* Content is passed by reference. Method is called before the content is saved.
*
* @param string $context The context of the content passed to the plugin (added in 1.6).
* @param object $article A \Joomla\CMS\Table\Table\ object.
* @param bool $isNew If the content is just about to be created.
*
* @return void
*
* @since 2.5
*/
public function onContentBeforeSave($context, $article, $isNew)
{
$this->importFinderPlugins();
// Trigger the onFinderBeforeSave event.
$this->getDispatcher()->dispatch('onFinderBeforeSave', new FinderEvent\BeforeSaveEvent('onFinderBeforeSave', [
'context' => $context,
'subject' => $article,
'isNew' => $isNew,
]));
}
/**
* Smart Search after delete content method.
* Content is passed by reference, but after the deletion.
*
* @param string $context The context of the content passed to the plugin (added in 1.6).
* @param object $article A \Joomla\CMS\Table\Table object.
*
* @return void
*
* @since 2.5
*/
public function onContentAfterDelete($context, $article): void
{
$this->importFinderPlugins();
// Trigger the onFinderAfterDelete event.
$this->getDispatcher()->dispatch('onFinderAfterDelete', new FinderEvent\AfterDeleteEvent('onFinderAfterDelete', [
'context' => $context,
'subject' => $article,
]));
}
/**
* Smart Search content state change method.
* Method to update the link information for items that have been changed
* from outside the edit screen. This is fired when the item is published,
* unpublished, archived, or unarchived from the list view.
*
* @param string $context The context for the content passed to the plugin.
* @param array $pks A list of primary key ids of the content that has changed state.
* @param integer $value The value of the state that the content has been changed to.
*
* @return void
*
* @since 2.5
*/
public function onContentChangeState($context, $pks, $value)
{
$this->importFinderPlugins();
// Trigger the onFinderChangeState event.
$this->getDispatcher()->dispatch('onFinderChangeState', new FinderEvent\AfterChangeStateEvent('onFinderChangeState', [
'context' => $context,
'subject' => $pks,
'value' => $value,
]));
}
/**
* Smart Search change category state content method.
* Method is called when the state of the category to which the
* content item belongs is changed.
*
* @param string $extension The extension whose category has been updated.
* @param array $pks A list of primary key ids of the content that has changed state.
* @param integer $value The value of the state that the content has been changed to.
*
* @return void
*
* @since 2.5
*/
public function onCategoryChangeState($extension, $pks, $value)
{
$this->importFinderPlugins();
// Trigger the onFinderCategoryChangeState event.
$this->getDispatcher()->dispatch('onFinderCategoryChangeState', new FinderEvent\AfterCategoryChangeStateEvent('onFinderCategoryChangeState', [
'context' => $extension,
'subject' => $pks,
'value' => $value,
]));
}
/**
* A helper method to import finder plugins.
*
* @return void
*
* @since 5.0.0
*/
protected function importFinderPlugins()
{
if ($this->pluginsImported) {
return;
}
$this->pluginsImported = true;
PluginHelper::importPlugin('finder', null, true, $this->getDispatcher());
}
}

View File

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="content" method="upgrade">
<name>plg_content_joomla</name>
<author>Joomla! Project</author>
<creationDate>2010-11</creationDate>
<copyright>(C) 2010 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>3.0.0</version>
<description>PLG_CONTENT_JOOMLA_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\Content\Joomla</namespace>
<files>
<folder plugin="joomla">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_content_joomla.ini</language>
<language tag="en-GB">language/en-GB/plg_content_joomla.sys.ini</language>
</languages>
<config>
<fields name="params">
<fieldset name="basic">
<field
name="check_categories"
type="radio"
layout="joomla.form.field.radio.switcher"
label="PLG_CONTENT_JOOMLA_FIELD_CHECK_CATEGORIES_LABEL"
description="PLG_CONTENT_JOOMLA_FIELD_CHECK_CATEGORIES_DESC"
default="1"
filter="integer"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
<field
name="email_new_fe"
type="radio"
label="PLG_CONTENT_JOOMLA_FIELD_EMAIL_NEW_FE_LABEL"
description="PLG_CONTENT_JOOMLA_FIELD_EMAIL_NEW_FE_DESC"
layout="joomla.form.field.radio.switcher"
default="1"
filter="integer"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
<field
name="schema_content"
type="radio"
label="PLG_CONTENT_JOOMLA_SCHEMA_CONTENT_LABEL"
description="PLG_CONTENT_JOOMLA_SCHEMA_CONTENT_DESC"
layout="joomla.form.field.radio.switcher"
default="1"
filter="integer"
required="true"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
<field
name="schema_contact"
type="radio"
label="PLG_CONTENT_JOOMLA_SCHEMA_CONTACT_LABEL"
description="PLG_CONTENT_JOOMLA_SCHEMA_CONTACT_DESC"
layout="joomla.form.field.radio.switcher"
default="1"
filter="integer"
required="true"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
</fieldset>
</fields>
</config>
</extension>

View File

@ -0,0 +1,50 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Content.joomla
*
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\User\UserFactoryInterface;
use Joomla\Database\DatabaseInterface;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Content\Joomla\Extension\Joomla;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.4.0
*/
public function register(Container $container): void
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new Joomla(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('content', 'joomla')
);
$plugin->setApplication(Factory::getApplication());
$plugin->setDatabase($container->get(DatabaseInterface::class));
$plugin->setUserFactory($container->get(UserFactoryInterface::class));
return $plugin;
}
);
}
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="content" method="upgrade">
<name>plg_content_loadmodule</name>
<author>Joomla! Project</author>
<creationDate>2005-11</creationDate>
<copyright>(C) 2005 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>3.0.0</version>
<description>PLG_LOADMODULE_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\Content\LoadModule</namespace>
<files>
<folder plugin="loadmodule">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_content_loadmodule.ini</language>
<language tag="en-GB">language/en-GB/plg_content_loadmodule.sys.ini</language>
</languages>
<config>
<fields name="params">
<fieldset name="basic">
<field
name="style"
type="list"
label="PLG_LOADMODULE_FIELD_STYLE_LABEL"
default="none"
validate="options"
>
<option value="none">PLG_LOADMODULE_FIELD_VALUE_RAW</option>
<option value="html5">PLG_LOADMODULE_FIELD_VALUE_DIVS</option>
<option value="table">PLG_LOADMODULE_FIELD_VALUE_TABLE</option>
<!-- @TODO: The following styles don't exist in default installation and can be removed in Joomla 5 -->
<option value="horz">PLG_LOADMODULE_FIELD_VALUE_HORIZONTAL</option>
<option value="rounded">PLG_LOADMODULE_FIELD_VALUE_MULTIPLEDIVS</option>
</field>
</fieldset>
</fields>
</config>
</extension>

View File

@ -0,0 +1,46 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Content.loadmodule
*
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Content\LoadModule\Extension\LoadModule;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.4.0
*/
public function register(Container $container): void
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new LoadModule(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('content', 'loadmodule')
);
$plugin->setApplication(Factory::getApplication());
return $plugin;
}
);
}
};

View File

@ -0,0 +1,250 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Content.loadmodule
*
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Plugin\Content\LoadModule\Extension;
use Joomla\CMS\Helper\ModuleHelper;
use Joomla\CMS\Plugin\CMSPlugin;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Plugin to enable loading modules into content (e.g. articles)
* This uses the {loadmodule} syntax
*
* @since 1.5
*/
final class LoadModule extends CMSPlugin
{
protected static $modules = [];
protected static $mods = [];
/**
* Plugin that loads module positions within content
*
* @param string $context The context of the content being passed to the plugin.
* @param object &$article The article object. Note $article->text is also available
* @param mixed &$params The article params
* @param integer $page The 'page' number
*
* @return void
*
* @since 1.6
*/
public function onContentPrepare($context, &$article, &$params, $page = 0)
{
// Only execute if $article is an object and has a text property
if (!\is_object($article) || !property_exists($article, 'text') || \is_null($article->text)) {
return;
}
$defaultStyle = $this->params->get('style', 'none');
// Fallback xhtml (used in Joomla 3) to html5
if ($defaultStyle === 'xhtml') {
$defaultStyle = 'html5';
}
// Expression to search for (positions)
$regex = '/{loadposition\s(.*?)}/i';
// Expression to search for(modules)
$regexmod = '/{loadmodule\s(.*?)}/i';
// Expression to search for(id)
$regexmodid = '/{loadmoduleid\s([1-9][0-9]*)}/i';
// Remove macros and don't run this plugin when the content is being indexed
if ($context === 'com_finder.indexer') {
if (str_contains($article->text, 'loadposition')) {
$article->text = preg_replace($regex, '', $article->text);
}
if (str_contains($article->text, 'loadmoduleid')) {
$article->text = preg_replace($regexmodid, '', $article->text);
}
if (str_contains($article->text, 'loadmodule')) {
$article->text = preg_replace($regexmod, '', $article->text);
}
return;
}
if (str_contains($article->text, '{loadposition ')) {
// Find all instances of plugin and put in $matches for loadposition
// $matches[0] is full pattern match, $matches[1] is the position
preg_match_all($regex, $article->text, $matches, PREG_SET_ORDER);
// No matches, skip this
if ($matches) {
foreach ($matches as $match) {
$matcheslist = explode(',', $match[1]);
// We may not have a module style so fall back to the plugin default.
if (!\array_key_exists(1, $matcheslist)) {
$matcheslist[1] = $defaultStyle;
}
$position = trim($matcheslist[0]);
$style = trim($matcheslist[1]);
$output = $this->load($position, $style);
// We should replace only first occurrence in order to allow positions with the same name to regenerate their content:
if (($start = strpos($article->text, $match[0])) !== false) {
$article->text = substr_replace($article->text, $output, $start, \strlen($match[0]));
}
}
}
}
if (str_contains($article->text, '{loadmodule ')) {
// Find all instances of plugin and put in $matchesmod for loadmodule
preg_match_all($regexmod, $article->text, $matchesmod, PREG_SET_ORDER);
// If no matches, skip this
if ($matchesmod) {
foreach ($matchesmod as $matchmod) {
$matchesmodlist = explode(',', $matchmod[1]);
// First parameter is the module, will be prefixed with mod_ later
$module = trim($matchesmodlist[0]);
// Second parameter is the title
$title = '';
if (\array_key_exists(1, $matchesmodlist)) {
$title = htmlspecialchars_decode(trim($matchesmodlist[1]));
}
// Third parameter is the module style, (fallback is the plugin default set earlier).
$stylemod = $defaultStyle;
if (\array_key_exists(2, $matchesmodlist)) {
$stylemod = trim($matchesmodlist[2]);
}
$output = $this->loadModule($module, $title, $stylemod);
// We should replace only first occurrence in order to allow positions with the same name to regenerate their content:
if (($start = strpos($article->text, $matchmod[0])) !== false) {
$article->text = substr_replace($article->text, $output, $start, \strlen($matchmod[0]));
}
}
}
}
if (str_contains($article->text, '{loadmoduleid ')) {
// Find all instances of plugin and put in $matchesmodid for loadmoduleid
preg_match_all($regexmodid, $article->text, $matchesmodid, PREG_SET_ORDER);
// If no matches, skip this
if ($matchesmodid) {
foreach ($matchesmodid as $match) {
$id = trim($match[1]);
$output = $this->loadID($id);
// We should replace only first occurrence in order to allow positions with the same name to regenerate their content:
if (($start = strpos($article->text, $match[0])) !== false) {
$article->text = substr_replace($article->text, $output, $start, \strlen($match[0]));
}
}
}
}
}
/**
* Loads and renders the module
*
* @param string $position The position assigned to the module
* @param string $style The style assigned to the module
*
* @return mixed
*
* @since 1.6
*/
private function load($position, $style = 'none')
{
$document = $this->getApplication()->getDocument();
$renderer = $document->loadRenderer('module');
$modules = ModuleHelper::getModules($position);
$params = ['style' => $style];
ob_start();
foreach ($modules as $module) {
echo $renderer->render($module, $params);
}
return ob_get_clean();
}
/**
* This is always going to get the first instance of the module type unless
* there is a title.
*
* @param string $module The module title
* @param string $title The title of the module
* @param string $style The style of the module
*
* @return mixed
*
* @since 1.6
*/
private function loadModule($module, $title, $style = 'none')
{
$document = $this->getApplication()->getDocument();
$renderer = $document->loadRenderer('module');
$mod = ModuleHelper::getModule($module, $title);
// If the module without the mod_ isn't found, try it with mod_.
// This allows people to enter it either way in the content
if (!isset($mod)) {
$name = 'mod_' . $module;
$mod = ModuleHelper::getModule($name, $title);
}
$params = ['style' => $style];
ob_start();
if ($mod->id) {
echo $renderer->render($mod, $params);
}
return ob_get_clean();
}
/**
* Loads and renders the module
*
* @param string $id The id of the module
*
* @return mixed
*
* @since 3.9.0
*/
private function loadID($id)
{
$document = $this->getApplication()->getDocument();
$renderer = $document->loadRenderer('module');
$modules = ModuleHelper::getModuleById($id);
$params = ['style' => 'none'];
ob_start();
if ($modules->id > 0) {
echo $renderer->render($modules, $params);
}
return ob_get_clean();
}
}

View File

@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="content" method="upgrade">
<name>plg_content_pagebreak</name>
<author>Joomla! Project</author>
<creationDate>2005-11</creationDate>
<copyright>(C) 2005 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>3.0.0</version>
<description>PLG_CONTENT_PAGEBREAK_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\Content\PageBreak</namespace>
<files>
<folder plugin="pagebreak">services</folder>
<folder>src</folder>
<folder>tmpl</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_content_pagebreak.ini</language>
<language tag="en-GB">language/en-GB/plg_content_pagebreak.sys.ini</language>
</languages>
<config>
<fields name="params">
<fieldset name="basic">
<field
name="title"
type="radio"
layout="joomla.form.field.radio.switcher"
label="PLG_CONTENT_PAGEBREAK_SITE_TITLE_LABEL"
default="1"
filter="integer"
>
<option value="0">JHIDE</option>
<option value="1">JSHOW</option>
</field>
<field
name="article_index"
type="radio"
layout="joomla.form.field.radio.switcher"
label="PLG_CONTENT_PAGEBREAK_SITE_ARTICLEINDEX_LABEL"
default="1"
filter="integer"
>
<option value="0">JHIDE</option>
<option value="1">JSHOW</option>
</field>
<field
name="article_index_text"
type="text"
label="PLG_CONTENT_PAGEBREAK_SITE_ARTICLEINDEXTEXT"
showon="article_index:1"
/>
<field
name="multipage_toc"
type="radio"
layout="joomla.form.field.radio.switcher"
label="PLG_CONTENT_PAGEBREAK_TOC_LABEL"
default="1"
filter="integer"
>
<option value="0">JHIDE</option>
<option value="1">JSHOW</option>
</field>
<field
name="showall"
type="radio"
layout="joomla.form.field.radio.switcher"
label="PLG_CONTENT_PAGEBREAK_SHOW_ALL_LABEL"
default="1"
filter="integer"
>
<option value="0">JHIDE</option>
<option value="1">JSHOW</option>
</field>
<field
name="style"
type="list"
label="PLG_CONTENT_PAGEBREAK_STYLE_LABEL"
default="pages"
validate="options"
>
<option value="pages">PLG_CONTENT_PAGEBREAK_PAGES</option>
<option value="sliders">PLG_CONTENT_PAGEBREAK_SLIDERS</option>
<option value="tabs">PLG_CONTENT_PAGEBREAK_TABS</option>
</field>
</fieldset>
</fields>
</config>
</extension>

View File

@ -0,0 +1,46 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Content.pagebreak
*
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Content\PageBreak\Extension\PageBreak;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.4.0
*/
public function register(Container $container): void
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new PageBreak(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('content', 'pagebreak')
);
$plugin->setApplication(Factory::getApplication());
return $plugin;
}
);
}
};

View File

@ -0,0 +1,358 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Content.pagebreak
*
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Plugin\Content\PageBreak\Extension;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Pagination\Pagination;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Utility\Utility;
use Joomla\Component\Content\Site\Helper\RouteHelper;
use Joomla\String\StringHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Page break plugin
*
* <strong>Usage:</strong>
* <code><hr class="system-pagebreak" /></code>
* <code><hr class="system-pagebreak" title="The page title" /></code>
* or
* <code><hr class="system-pagebreak" alt="The first page" /></code>
* or
* <code><hr class="system-pagebreak" title="The page title" alt="The first page" /></code>
* or
* <code><hr class="system-pagebreak" alt="The first page" title="The page title" /></code>
*
* @since 1.6
*/
final class PageBreak extends CMSPlugin
{
/**
* The navigation list with all page objects if parameter 'multipage_toc' is active.
*
* @var array
* @since 4.0.0
*/
protected $list = [];
/**
* Plugin that adds a pagebreak into the text and truncates text at that point
*
* @param string $context The context of the content being passed to the plugin.
* @param object &$row The article object. Note $article->text is also available
* @param mixed &$params The article params
* @param integer $page The 'page' number
*
* @return void
*
* @since 1.6
*/
public function onContentPrepare($context, &$row, &$params, $page = 0)
{
$canProceed = $context === 'com_content.article';
if (!$canProceed) {
return;
}
$style = $this->params->get('style', 'pages');
// Expression to search for.
$regex = '#<hr(.*)class="system-pagebreak"(.*)\/?>#iU';
$input = $this->getApplication()->getInput();
$print = $input->getBool('print');
$showall = $input->getBool('showall');
if (!$this->params->get('enabled', 1)) {
$print = true;
}
if ($print) {
$row->text = preg_replace($regex, '<br>', $row->text);
return;
}
// Simple performance check to determine whether bot should process further.
if (StringHelper::strpos($row->text, 'class="system-pagebreak') === false) {
if ($page > 0) {
throw new \Exception($this->getApplication()->getLanguage()->_('JERROR_PAGE_NOT_FOUND'), 404);
}
return;
}
$view = $input->getString('view');
$full = $input->getBool('fullview');
if (!$page) {
$page = 0;
}
if ($full || $view !== 'article' || $params->get('intro_only') || $params->get('popup')) {
$row->text = preg_replace($regex, '', $row->text);
return;
}
// Load plugin language files only when needed (ex: not needed if no system-pagebreak class exists).
$this->loadLanguage();
// Find all instances of plugin and put in $matches.
$matches = [];
preg_match_all($regex, $row->text, $matches, PREG_SET_ORDER);
if ($showall && $this->params->get('showall', 1)) {
$hasToc = $this->params->get('multipage_toc', 1);
if ($hasToc) {
// Display TOC.
$page = 1;
$this->createToc($row, $matches, $page);
} else {
$row->toc = '';
}
$row->text = preg_replace($regex, '<br>', $row->text);
return;
}
// Split the text around the plugin.
$text = preg_split($regex, $row->text);
if (!isset($text[$page])) {
throw new \Exception($this->getApplication()->getLanguage()->_('JERROR_PAGE_NOT_FOUND'), 404);
}
// Count the number of pages.
$n = \count($text);
// We have found at least one plugin, therefore at least 2 pages.
if ($n > 1) {
$title = $this->params->get('title', 1);
$hasToc = $this->params->get('multipage_toc', 1);
// Adds heading or title to <site> Title.
if ($title && $page && isset($matches[$page - 1][0])) {
$attrs = Utility::parseAttributes($matches[$page - 1][0]);
if (isset($attrs['title'])) {
$row->page_title = $attrs['title'];
}
}
// Reset the text, we already hold it in the $text array.
$row->text = '';
if ($style === 'pages') {
// Display TOC.
if ($hasToc) {
$this->createToc($row, $matches, $page);
} else {
$row->toc = '';
}
// Traditional mos page navigation
$pageNav = new Pagination($n, $page, 1);
// Flag indicates to not add limitstart=0 to URL
$pageNav->hideEmptyLimitstart = true;
// Page counter.
$row->text .= '<div class="pagenavcounter">';
$row->text .= $pageNav->getPagesCounter();
$row->text .= '</div>';
// Page text.
$text[$page] = str_replace('<hr id="system-readmore" />', '', $text[$page]);
$row->text .= $text[$page];
// $row->text .= '<br>';
$row->text .= '<div class="pager">';
// Adds navigation between pages to bottom of text.
if ($hasToc) {
$this->createNavigation($row, $page, $n);
}
// Page links shown at bottom of page if TOC disabled.
if (!$hasToc) {
$row->text .= $pageNav->getPagesLinks();
}
$row->text .= '</div>';
} else {
$t[] = $text[0];
if ($style === 'tabs') {
$t[] = (string) HTMLHelper::_('uitab.startTabSet', 'myTab', ['active' => 'article' . $row->id . '-' . $style . '0', 'view' => 'tabs']);
} else {
$t[] = (string) HTMLHelper::_('bootstrap.startAccordion', 'myAccordion', ['active' => 'article' . $row->id . '-' . $style . '0']);
}
foreach ($text as $key => $subtext) {
$index = 'article' . $row->id . '-' . $style . $key;
if ($key >= 1) {
$match = $matches[$key - 1];
$match = (array) Utility::parseAttributes($match[0]);
if (isset($match['alt'])) {
$title = stripslashes($match['alt']);
} elseif (isset($match['title'])) {
$title = stripslashes($match['title']);
} else {
$title = Text::sprintf('PLG_CONTENT_PAGEBREAK_PAGE_NUM', $key + 1);
}
if ($style === 'tabs') {
$t[] = (string) HTMLHelper::_('uitab.addTab', 'myTab', $index, $title);
} else {
$t[] = (string) HTMLHelper::_('bootstrap.addSlide', 'myAccordion', $title, $index);
}
$t[] = (string) $subtext;
if ($style === 'tabs') {
$t[] = (string) HTMLHelper::_('uitab.endTab');
} else {
$t[] = (string) HTMLHelper::_('bootstrap.endSlide');
}
}
}
if ($style === 'tabs') {
$t[] = (string) HTMLHelper::_('uitab.endTabSet');
} else {
$t[] = (string) HTMLHelper::_('bootstrap.endAccordion');
}
$row->text = implode(' ', $t);
}
}
}
/**
* Creates a Table of Contents for the pagebreak
*
* @param object &$row The article object. Note $article->text is also available
* @param array &$matches Array of matches of a regex in onContentPrepare
* @param integer &$page The 'page' number
*
* @return void
*
* @since 1.6
*/
private function createToc(&$row, &$matches, &$page)
{
$heading = $row->title ?? $this->getApplication()->getLanguage()->_('PLG_CONTENT_PAGEBREAK_NO_TITLE');
$input = $this->getApplication()->getInput();
$limitstart = $input->getUint('limitstart', 0);
$showall = $input->getInt('showall', 0);
$headingtext = '';
if ($this->params->get('article_index', 1) == 1) {
$headingtext = $this->getApplication()->getLanguage()->_('PLG_CONTENT_PAGEBREAK_ARTICLE_INDEX');
if ($this->params->get('article_index_text')) {
$headingtext = htmlspecialchars($this->params->get('article_index_text'), ENT_QUOTES, 'UTF-8');
}
}
// TOC first Page link.
$this->list[1] = new \stdClass();
$this->list[1]->link = RouteHelper::getArticleRoute($row->slug, $row->catid, $row->language);
$this->list[1]->title = $heading;
$this->list[1]->active = ($limitstart === 0 && $showall === 0);
$i = 2;
foreach ($matches as $bot) {
if (@$bot[0]) {
$attrs2 = Utility::parseAttributes($bot[0]);
if (@$attrs2['alt']) {
$title = stripslashes($attrs2['alt']);
} elseif (@$attrs2['title']) {
$title = stripslashes($attrs2['title']);
} else {
$title = Text::sprintf('PLG_CONTENT_PAGEBREAK_PAGE_NUM', $i);
}
} else {
$title = Text::sprintf('PLG_CONTENT_PAGEBREAK_PAGE_NUM', $i);
}
$this->list[$i] = new \stdClass();
$this->list[$i]->link = RouteHelper::getArticleRoute($row->slug, $row->catid, $row->language) . '&limitstart=' . ($i - 1);
$this->list[$i]->title = $title;
$this->list[$i]->active = ($limitstart === $i - 1);
$i++;
}
if ($this->params->get('showall')) {
$this->list[$i] = new \stdClass();
$this->list[$i]->link = RouteHelper::getArticleRoute($row->slug, $row->catid, $row->language) . '&showall=1';
$this->list[$i]->title = $this->getApplication()->getLanguage()->_('PLG_CONTENT_PAGEBREAK_ALL_PAGES');
$this->list[$i]->active = ($limitstart === $i - 1);
}
$list = $this->list;
$path = PluginHelper::getLayoutPath('content', 'pagebreak', 'toc');
ob_start();
include $path;
$row->toc = ob_get_clean();
}
/**
* Creates the navigation for the item
*
* @param object &$row The article object. Note $article->text is also available
* @param int $page The page number
* @param int $n The total number of pages
*
* @return void
*
* @since 1.6
*/
private function createNavigation(&$row, $page, $n)
{
$links = [
'next' => '',
'previous' => '',
];
if ($page < $n - 1) {
$links['next'] = RouteHelper::getArticleRoute($row->slug, $row->catid, $row->language) . '&limitstart=' . ($page + 1);
}
if ($page > 0) {
$links['previous'] = RouteHelper::getArticleRoute($row->slug, $row->catid, $row->language);
if ($page > 1) {
$links['previous'] .= '&limitstart=' . ($page - 1);
}
}
$path = PluginHelper::getLayoutPath('content', 'pagebreak', 'navigation');
ob_start();
include $path;
$row->text .= ob_get_clean();
}
}

View File

@ -0,0 +1,46 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Content.pagebreak
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;
/**
* @var $links array Array with keys 'previous' and 'next' with non-SEO links to the previous and next pages
* @var $page integer The page number
*/
$lang = $this->getApplication()->getLanguage();
?>
<ul class="pagination">
<li class="previous page-item">
<?php if ($links['previous']) :
$direction = $lang->isRtl() ? 'right' : 'left';
$title = htmlspecialchars($this->list[$page]->title, ENT_QUOTES, 'UTF-8');
$ariaLabel = Text::_('JPREVIOUS') . ': ' . $title . ' (' . Text::sprintf('JLIB_HTML_PAGE_CURRENT_OF_TOTAL', $page, $n) . ')';
?>
<a class="page-link" href="<?php echo Route::_($links['previous']); ?>" title="<?php echo $title; ?>" aria-label="<?php echo $ariaLabel; ?>" rel="prev">
<?php echo '<span class="icon-chevron-' . $direction . '" aria-hidden="true"></span> ' . Text::_('JPREV'); ?>
</a>
<?php endif; ?>
</li>
<li class="next page-item">
<?php if ($links['next']) :
$direction = $lang->isRtl() ? 'left' : 'right';
$title = htmlspecialchars($this->list[$page + 2]->title, ENT_QUOTES, 'UTF-8');
$ariaLabel = Text::_('JNEXT') . ': ' . $title . ' (' . Text::sprintf('JLIB_HTML_PAGE_CURRENT_OF_TOTAL', ($page + 2), $n) . ')';
?>
<a class="page-link" href="<?php echo Route::_($links['next']); ?>" title="<?php echo $title; ?>" aria-label="<?php echo $ariaLabel; ?>" rel="next">
<?php echo Text::_('JNEXT') . ' <span class="icon-chevron-' . $direction . '" aria-hidden="true"></span>'; ?>
</a>
<?php endif; ?>
</li>
</ul>

View File

@ -0,0 +1,34 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Content.pagebreak
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\Router\Route;
?>
<div class="card float-end article-index ms-3 mb-3">
<div class="card-body">
<?php if ($headingtext) : ?>
<h3><?php echo $headingtext; ?></h3>
<?php endif; ?>
<ul class="nav flex-column">
<?php foreach ($list as $listItem) : ?>
<?php $class = $listItem->active ? ' active' : ''; ?>
<li class="py-1">
<a href="<?php echo Route::_($listItem->link); ?>" class="toclink<?php echo $class; ?>">
<?php echo $listItem->title; ?>
</a>
</li>
<?php endforeach; ?>
</ul>
</div>
</div>

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="content" method="upgrade">
<name>plg_content_pagenavigation</name>
<author>Joomla! Project</author>
<creationDate>2006-01</creationDate>
<copyright>(C) 2006 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>3.0.0</version>
<description>PLG_PAGENAVIGATION_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\Content\PageNavigation</namespace>
<files>
<folder plugin="pagenavigation">services</folder>
<folder>src</folder>
<folder>tmpl</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_content_pagenavigation.ini</language>
<language tag="en-GB">language/en-GB/plg_content_pagenavigation.sys.ini</language>
</languages>
<config>
<fields name="params">
<fieldset name="basic">
<field
name="position"
type="list"
label="PLG_PAGENAVIGATION_FIELD_POSITION_LABEL"
default="1"
filter="integer"
validate="options"
>
<option value="1">PLG_PAGENAVIGATION_FIELD_VALUE_BELOW</option>
<option value="0">PLG_PAGENAVIGATION_FIELD_VALUE_ABOVE</option>
</field>
<field
name="relative"
type="list"
label="PLG_PAGENAVIGATION_FIELD_RELATIVE_LABEL"
default="1"
filter="integer"
validate="options"
>
<option value="1">PLG_PAGENAVIGATION_FIELD_VALUE_ARTICLE</option>
<option value="0">PLG_PAGENAVIGATION_FIELD_VALUE_TEXT</option>
</field>
<field
name="display"
type="list"
label="PLG_PAGENAVIGATION_FIELD_DISPLAY_LABEL"
default="0"
filter="integer"
validate="options"
>
<option value="0">PLG_PAGENAVIGATION_FIELD_VALUE_NEXTPREV</option>
<option value="1">PLG_PAGENAVIGATION_FIELD_VALUE_TITLE</option>
</field>
</fieldset>
</fields>
</config>
</extension>

View File

@ -0,0 +1,48 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Content.pagenavigation
*
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\Database\DatabaseInterface;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Content\PageNavigation\Extension\PageNavigation;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.4.0
*/
public function register(Container $container): void
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new PageNavigation(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('content', 'pagenavigation')
);
$plugin->setApplication(Factory::getApplication());
$plugin->setDatabase($container->get(DatabaseInterface::class));
return $plugin;
}
);
}
};

View File

@ -0,0 +1,247 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Content.pagenavigation
*
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Plugin\Content\PageNavigation\Extension;
use Joomla\CMS\Access\Access;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\Component\Content\Site\Helper\RouteHelper;
use Joomla\Database\DatabaseAwareTrait;
use Joomla\Database\ParameterType;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Pagenavigation plugin class.
*
* @since 1.5
*/
final class PageNavigation extends CMSPlugin
{
use DatabaseAwareTrait;
/**
* If in the article view and the parameter is enabled shows the page navigation
*
* @param string $context The context of the content being passed to the plugin
* @param object &$row The article object
* @param mixed &$params The article params
* @param integer $page The 'page' number
*
* @return void
*
* @since 1.6
*/
public function onContentBeforeDisplay($context, &$row, &$params, $page = 0)
{
$app = $this->getApplication();
$view = $app->getInput()->get('view');
$print = $app->getInput()->getBool('print');
if ($print) {
return;
}
if ($context === 'com_content.article' && $view === 'article' && $params->get('show_item_navigation')) {
$db = $this->getDatabase();
$user = $app->getIdentity();
$lang = $app->getLanguage();
$now = Factory::getDate()->toSql();
$query = $db->getQuery(true);
$uid = $row->id;
$option = 'com_content';
$canPublish = $user->authorise('core.edit.state', $option . '.article.' . $row->id);
/**
* The following is needed as different menu items types utilise a different param to control ordering.
* For Blogs the `orderby_sec` param is the order controlling param.
* For Table and List views it is the `orderby` param.
*/
$params_list = $params->toArray();
if (\array_key_exists('orderby_sec', $params_list)) {
$order_method = $params->get('orderby_sec', '');
} else {
$order_method = $params->get('orderby', '');
}
// Additional check for invalid sort ordering.
if ($order_method === 'front') {
$order_method = '';
}
if (\in_array($order_method, ['date', 'rdate'])) {
// Get the order code
$orderDate = $params->get('order_date');
switch ($orderDate) {
// Use created if modified is not set
case 'modified':
$orderby = 'CASE WHEN ' . $db->quoteName('a.modified') . ' IS NULL THEN ' .
$db->quoteName('a.created') . ' ELSE ' . $db->quoteName('a.modified') . ' END';
break;
// Use created if publish_up is not set
case 'published':
$orderby = 'CASE WHEN ' . $db->quoteName('a.publish_up') . ' IS NULL THEN ' .
$db->quoteName('a.created') . ' ELSE ' . $db->quoteName('a.publish_up') . ' END';
break;
// Use created as default
default:
$orderby = $db->quoteName('a.created');
break;
}
if ($order_method === 'rdate') {
$orderby .= ' DESC';
}
} else {
// Determine sort order.
switch ($order_method) {
case 'alpha':
$orderby = $db->quoteName('a.title');
break;
case 'ralpha':
$orderby = $db->quoteName('a.title') . ' DESC';
break;
case 'hits':
$orderby = $db->quoteName('a.hits');
break;
case 'rhits':
$orderby = $db->quoteName('a.hits') . ' DESC';
break;
case 'author':
$orderby = $db->quoteName(['a.created_by_alias', 'u.name']);
break;
case 'rauthor':
$orderby = $db->quoteName('a.created_by_alias') . ' DESC, ' .
$db->quoteName('u.name') . ' DESC';
break;
case 'front':
$orderby = $db->quoteName('f.ordering');
break;
default:
$orderby = $db->quoteName('a.ordering');
break;
}
}
$query->order($orderby);
$case_when = ' CASE WHEN ' . $query->charLength($db->quoteName('a.alias'), '!=', '0')
. ' THEN ' . $query->concatenate([$query->castAsChar($db->quoteName('a.id')), $db->quoteName('a.alias')], ':')
. ' ELSE ' . $query->castAsChar('a.id') . ' END AS ' . $db->quoteName('slug');
$case_when1 = ' CASE WHEN ' . $query->charLength($db->quoteName('cc.alias'), '!=', '0')
. ' THEN ' . $query->concatenate([$query->castAsChar($db->quoteName('cc.id')), $db->quoteName('cc.alias')], ':')
. ' ELSE ' . $query->castAsChar('cc.id') . ' END AS ' . $db->quoteName('catslug');
$query->select($db->quoteName(['a.id', 'a.title', 'a.catid', 'a.language']))
->select([$case_when, $case_when1])
->from($db->quoteName('#__content', 'a'))
->join('LEFT', $db->quoteName('#__categories', 'cc'), $db->quoteName('cc.id') . ' = ' . $db->quoteName('a.catid'));
if ($order_method === 'author' || $order_method === 'rauthor') {
$query->select($db->quoteName(['a.created_by', 'u.name']));
$query->join('LEFT', $db->quoteName('#__users', 'u'), $db->quoteName('u.id') . ' = ' . $db->quoteName('a.created_by'));
}
$query->where(
[
$db->quoteName('a.catid') . ' = :catid',
$db->quoteName('a.state') . ' = :state',
]
)
->bind(':catid', $row->catid, ParameterType::INTEGER)
->bind(':state', $row->state, ParameterType::INTEGER);
if (!$canPublish) {
$query->whereIn($db->quoteName('a.access'), Access::getAuthorisedViewLevels($user->id));
}
$query->where(
[
'(' . $db->quoteName('publish_up') . ' IS NULL OR ' . $db->quoteName('publish_up') . ' <= :nowDate1)',
'(' . $db->quoteName('publish_down') . ' IS NULL OR ' . $db->quoteName('publish_down') . ' >= :nowDate2)',
]
)
->bind(':nowDate1', $now)
->bind(':nowDate2', $now);
if ($app->isClient('site') && $app->getLanguageFilter()) {
$query->whereIn($db->quoteName('a.language'), [$lang->getTag(), '*'], ParameterType::STRING);
}
$db->setQuery($query);
$list = $db->loadObjectList('id');
// This check needed if incorrect Itemid is given resulting in an incorrect result.
if (!\is_array($list)) {
$list = [];
}
reset($list);
// Location of current content item in array list.
$location = array_search($uid, array_keys($list));
$rows = array_values($list);
$row->prev = null;
$row->next = null;
if ($location - 1 >= 0) {
// The previous content item cannot be in the array position -1.
$row->prev = $rows[$location - 1];
}
if (($location + 1) < \count($rows)) {
// The next content item cannot be in an array position greater than the number of array positions.
$row->next = $rows[$location + 1];
}
if ($row->prev) {
$row->prev_label = ($this->params->get('display', 0) == 0) ? $lang->_('JPREV') : $row->prev->title;
$row->prev = RouteHelper::getArticleRoute($row->prev->slug, $row->prev->catid, $row->prev->language);
} else {
$row->prev_label = '';
$row->prev = '';
}
if ($row->next) {
$row->next_label = ($this->params->get('display', 0) == 0) ? $lang->_('JNEXT') : $row->next->title;
$row->next = RouteHelper::getArticleRoute($row->next->slug, $row->next->catid, $row->next->language);
} else {
$row->next_label = '';
$row->next = '';
}
// Output.
if ($row->prev || $row->next) {
// Get the path for the layout file
$path = PluginHelper::getLayoutPath('content', 'pagenavigation');
// Render the pagenav
ob_start();
include $path;
$row->pagination = ob_get_clean();
$row->paginationposition = $this->params->get('position', 1);
// This will default to the 1.5 and 1.6-1.7 behavior.
$row->paginationrelative = $this->params->get('relative', 0);
}
}
}
}

View File

@ -0,0 +1,40 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Content.pagenavigation
*
* @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;
$lang = $this->getLanguage();
?>
<nav class="pagenavigation">
<span class="pagination ms-0">
<?php if ($row->prev) :
$direction = $lang->isRtl() ? 'right' : 'left'; ?>
<a class="btn btn-sm btn-secondary previous" href="<?php echo Route::_($row->prev); ?>" rel="prev">
<span class="visually-hidden">
<?php echo Text::sprintf('JPREVIOUS_TITLE', htmlspecialchars($rows[$location - 1]->title)); ?>
</span>
<?php echo '<span class="icon-chevron-' . $direction . '" aria-hidden="true"></span> <span aria-hidden="true">' . $row->prev_label . '</span>'; ?>
</a>
<?php endif; ?>
<?php if ($row->next) :
$direction = $lang->isRtl() ? 'left' : 'right'; ?>
<a class="btn btn-sm btn-secondary next" href="<?php echo Route::_($row->next); ?>" rel="next">
<span class="visually-hidden">
<?php echo Text::sprintf('JNEXT_TITLE', htmlspecialchars($rows[$location + 1]->title)); ?>
</span>
<?php echo '<span aria-hidden="true">' . $row->next_label . '</span> <span class="icon-chevron-' . $direction . '" aria-hidden="true"></span>'; ?>
</a>
<?php endif; ?>
</span>
</nav>

View File

@ -0,0 +1,46 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Content.vote
*
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Content\Vote\Extension\Vote;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.4.0
*/
public function register(Container $container): void
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new Vote(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('content', 'vote')
);
$plugin->setApplication(Factory::getApplication());
return $plugin;
}
);
}
};

View File

@ -0,0 +1,126 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Content.vote
*
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Plugin\Content\Vote\Extension;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Plugin\PluginHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Vote plugin.
*
* @since 1.5
*/
final class Vote extends CMSPlugin
{
/**
* @var \Joomla\CMS\Application\CMSApplication
*
* @since 3.7.0
*
* @deprecated 4.4.0 will be removed in 6.0 as it is there only for layout overrides
* Use getApplication() instead
*/
protected $app;
/**
* Displays the voting area when viewing an article and the voting section is displayed before the article
*
* @param string $context The context of the content being passed to the plugin
* @param object &$row The article object
* @param object &$params The article params
* @param integer $page The 'page' number
*
* @return string|boolean HTML string containing code for the votes if in com_content else boolean false
*
* @since 1.6
*/
public function onContentBeforeDisplay($context, &$row, &$params, $page = 0)
{
if ($this->params->get('position', 'top') !== 'top') {
return '';
}
return $this->displayVotingData($context, $row, $params, $page);
}
/**
* Displays the voting area when viewing an article and the voting section is displayed after the article
*
* @param string $context The context of the content being passed to the plugin
* @param object &$row The article object
* @param object &$params The article params
* @param integer $page The 'page' number
*
* @return string|boolean HTML string containing code for the votes if in com_content else boolean false
*
* @since 3.7.0
*/
public function onContentAfterDisplay($context, &$row, &$params, $page = 0)
{
if ($this->params->get('position', 'top') !== 'bottom') {
return '';
}
return $this->displayVotingData($context, $row, $params, $page);
}
/**
* Displays the voting area
*
* @param string $context The context of the content being passed to the plugin
* @param object &$row The article object
* @param object &$params The article params
* @param integer $page The 'page' number
*
* @return string HTML string containing code for the votes if in com_content else empty string
*
* @since 3.7.0
*/
private function displayVotingData($context, &$row, &$params, $page)
{
$parts = explode('.', $context);
if ($parts[0] !== 'com_content') {
return '';
}
if (empty($params) || !$params->get('show_vote', null)) {
return '';
}
// Load plugin language files only when needed (ex: they are not needed if show_vote is not active).
$this->loadLanguage();
// Get the path for the rating summary layout file
$path = PluginHelper::getLayoutPath('content', 'vote', 'rating');
// Render the layout
ob_start();
include $path;
$html = ob_get_clean();
if ($this->getApplication()->getInput()->getString('view', '') === 'article' && $row->state == 1) {
// Get the path for the voting form layout file
$path = PluginHelper::getLayoutPath('content', 'vote', 'vote');
// Render the layout
ob_start();
include $path;
$html .= ob_get_clean();
}
return $html;
}
}

View File

@ -0,0 +1,94 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Content.vote
*
* @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Uri\Uri;
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->getApplication()->getDocument()->getWebAssetManager();
$wa->registerAndUseStyle('plg_content_vote', 'plg_content_vote/rating.css');
/**
* Layout variables
* -----------------
* @var string $context The context of the content being passed to the plugin
* @var object &$row The article object
* @var object &$params The article params
* @var integer $page The 'page' number
* @var array $parts The context segments
* @var string $path Path to this file
*/
if ($context === 'com_content.categories') {
return;
}
// Get the icons
$iconStar = HTMLHelper::_('image', 'plg_content_vote/vote-star.svg', '', '', true, true);
$iconHalfstar = HTMLHelper::_('image', 'plg_content_vote/vote-star-half.svg', '', '', true, true);
// If you can't find the icons then skip it
if ($iconStar === null || $iconHalfstar === null) {
return;
}
// Get paths to icons
$pathStar = JPATH_ROOT . substr($iconStar, strlen(Uri::root(true)));
$pathHalfstar = JPATH_ROOT . substr($iconHalfstar, strlen(Uri::root(true)));
// Write inline '<svg>' elements
$star = file_exists($pathStar) ? file_get_contents($pathStar) : '';
$halfstar = file_exists($pathHalfstar) ? file_get_contents($pathHalfstar) : '';
// Get rating
$rating = (float) $row->rating;
$rcount = (int) $row->rating_count;
// Round to 0.5
$rating = round($rating / 0.5) * 0.5;
// Determine number of stars
$stars = $rating;
$img = '';
for ($i = 0; $i < floor($stars); $i++) {
$img .= '<li class="vote-star">' . $star . '</li>';
}
if (($stars - floor($stars)) >= 0.5) {
$img .= '<li class="vote-star-empty">' . $star . '</li>';
$img .= '<li class="vote-star-half">' . $halfstar . '</li>';
++$stars;
}
for ($i = $stars; $i < 5; $i++) {
$img .= '<li class="vote-star-empty">' . $star . '</li>';
}
?>
<div class="content_rating" role="img" aria-label="<?php echo Text::sprintf('PLG_VOTE_STAR_RATING', $rating); ?>">
<?php if ($rcount) : ?>
<p class="visually-hidden" itemprop="aggregateRating" itemscope itemtype="https://schema.org/AggregateRating">
<?php echo Text::sprintf('PLG_VOTE_USER_RATING', '<span itemprop="ratingValue">' . $rating . '</span>', '<span itemprop="bestRating">5</span>'); ?>
<meta itemprop="ratingCount" content="<?php echo $rcount; ?>">
<meta itemprop="worstRating" content="1">
</p>
<?php if ($this->params->get('show_total_votes', 0)) : ?>
<?php echo Text::sprintf('PLG_VOTE_TOTAL_VOTES', $rcount); ?>
<?php endif; ?>
<?php endif; ?>
<ul>
<?php echo $img; ?>
</ul>
</div>

View File

@ -0,0 +1,48 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Content.vote
*
* @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Uri\Uri;
/**
* Layout variables
* -----------------
* @var string $context The context of the content being passed to the plugin
* @var object &$row The article object
* @var object &$params The article params
* @var integer $page The 'page' number
* @var array $parts The context segments
* @var string $path Path to this file
*/
$uri = clone Uri::getInstance();
// Create option list for voting select box
$options = [];
for ($i = 1; $i < 6; $i++) {
$options[] = HTMLHelper::_('select.option', $i, Text::sprintf('PLG_VOTE_VOTE', $i));
}
?>
<form method="post" action="<?php echo htmlspecialchars($uri->toString(), ENT_COMPAT, 'UTF-8'); ?>" class="form-inline mb-2">
<span class="content_vote">
<label class="visually-hidden" for="content_vote_<?php echo (int) $row->id; ?>"><?php echo Text::_('PLG_VOTE_LABEL'); ?></label>
<?php echo HTMLHelper::_('select.genericlist', $options, 'user_rating', 'class="form-select form-select-sm w-auto"', 'value', 'text', '5', 'content_vote_' . (int) $row->id); ?>
<input class="btn btn-sm btn-primary align-baseline" type="submit" name="submit_vote" value="<?php echo Text::_('PLG_VOTE_RATE'); ?>">
<input type="hidden" name="task" value="article.vote">
<input type="hidden" name="hitcount" value="0">
<input type="hidden" name="url" value="<?php echo htmlspecialchars($uri->toString(), ENT_COMPAT, 'UTF-8'); ?>">
<?php echo HTMLHelper::_('form.token'); ?>
</span>
</form>

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="content" method="upgrade">
<name>plg_content_vote</name>
<author>Joomla! Project</author>
<creationDate>2005-11</creationDate>
<copyright>(C) 2005 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>3.0.0</version>
<description>PLG_VOTE_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\Content\Vote</namespace>
<files>
<folder plugin="vote">services</folder>
<folder>src</folder>
<folder>tmpl</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_content_vote.ini</language>
<language tag="en-GB">language/en-GB/plg_content_vote.sys.ini</language>
</languages>
<config>
<fields name="params">
<fieldset name="basic">
<field
name="position"
type="list"
label="PLG_VOTE_POSITION_LABEL"
default="top"
validate="options"
>
<option value="top">PLG_VOTE_TOP</option>
<option value="bottom">PLG_VOTE_BOTTOM</option>
</field>
<field
name="show_total_votes"
type="radio"
label="PLG_VOTE_TOTAL_VOTES_LABEL"
layout="joomla.form.field.radio.switcher"
default="0"
filter="integer"
>
<option value="0">JHIDE</option>
<option value="1">JSHOW</option>
</field>
</fieldset>
</fields>
</config>
</extension>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="editors-xtd" method="upgrade">
<name>plg_editors-xtd_article</name>
<author>Joomla! Project</author>
<creationDate>2009-10</creationDate>
<copyright>(C) 2009 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>3.0.0</version>
<description>PLG_ARTICLE_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\EditorsXtd\Article</namespace>
<files>
<folder plugin="article">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_editors-xtd_article.ini</language>
<language tag="en-GB">language/en-GB/plg_editors-xtd_article.sys.ini</language>
</languages>
</extension>

View File

@ -0,0 +1,46 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Editors-xtd.article
*
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\EditorsXtd\Article\Extension\Article;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.3.0
*/
public function register(Container $container)
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new Article(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('editors-xtd', 'article')
);
$plugin->setApplication(Factory::getApplication());
return $plugin;
}
);
}
};

View File

@ -0,0 +1,115 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Editors-xtd.article
*
* @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Plugin\EditorsXtd\Article\Extension;
use Joomla\CMS\Editor\Button\Button;
use Joomla\CMS\Event\Editor\EditorButtonsSetupEvent;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Session\Session;
use Joomla\Event\SubscriberInterface;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Editor Article button
*
* @since 1.5
*/
final class Article extends CMSPlugin implements SubscriberInterface
{
/**
* Returns an array of events this subscriber will listen to.
*
* @return array
*
* @since 5.0.0
*/
public static function getSubscribedEvents(): array
{
return ['onEditorButtonsSetup' => 'onEditorButtonsSetup'];
}
/**
* @param EditorButtonsSetupEvent $event
* @return void
*
* @since 5.0.0
*/
public function onEditorButtonsSetup(EditorButtonsSetupEvent $event)
{
$subject = $event->getButtonsRegistry();
$disabled = $event->getDisabledButtons();
if (\in_array($this->_name, $disabled)) {
return;
}
$this->loadLanguage();
$button = $this->onDisplay($event->getEditorId());
if ($button) {
$subject->add($button);
}
}
/**
* Display the button
*
* @param string $name The name of the button to add
*
* @return Button|void The button options as Button object, void if ACL check fails.
*
* @since 1.5
*
* @deprecated 6.0 Use onEditorButtonsSetup event
*/
public function onDisplay($name)
{
$user = $this->getApplication()->getIdentity();
// Can create in any category (component permission) or at least in one category
$canCreateRecords = $user->authorise('core.create', 'com_content')
|| \count($user->getAuthorisedCategories('com_content', 'core.create')) > 0;
// Instead of checking edit on all records, we can use **same** check as the form editing view
$values = (array) $this->getApplication()->getUserState('com_content.edit.article.id');
$isEditingRecords = \count($values);
// This ACL check is probably a double-check (form view already performed checks)
$hasAccess = $canCreateRecords || $isEditingRecords;
if (!$hasAccess) {
return;
}
$link = 'index.php?option=com_content&view=articles&layout=modal&tmpl=component&'
. Session::getFormToken() . '=1&editor=' . $name;
$button = new Button(
$this->_name,
[
'action' => 'modal',
'link' => $link,
'text' => Text::_('PLG_ARTICLE_BUTTON_ARTICLE'),
'icon' => 'file-add',
'iconSVG' => '<svg viewBox="0 0 32 32" width="24" height="24"><path d="M28 24v-4h-4v4h-4v4h4v4h4v-4h4v-4zM2 2h18v6h6v10h2v-10l-8-'
. '8h-20v32h18v-2h-16z"></path></svg>',
// This is whole Plugin name, it is needed for keeping backward compatibility
'name' => $this->_type . '_' . $this->_name,
]
);
return $button;
}
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="editors-xtd" method="upgrade">
<name>plg_editors-xtd_contact</name>
<author>Joomla! Project</author>
<creationDate>2016-10</creationDate>
<copyright>(C) 2016 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>3.7.0</version>
<description>PLG_EDITORS-XTD_CONTACT_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\EditorsXtd\Contact</namespace>
<files>
<folder plugin="contact">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_editors-xtd_contact.ini</language>
<language tag="en-GB">language/en-GB/plg_editors-xtd_contact.sys.ini</language>
</languages>
</extension>

View File

@ -0,0 +1,46 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Editors-xtd.contact
*
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\EditorsXtd\Contact\Extension\Contact;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.3.0
*/
public function register(Container $container)
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new Contact(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('editors-xtd', 'contact')
);
$plugin->setApplication(Factory::getApplication());
return $plugin;
}
);
}
};

View File

@ -0,0 +1,75 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Editors-xtd.contact
*
* @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\Plugin\EditorsXtd\Contact\Extension;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Object\CMSObject;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Session\Session;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Editor Contact button
*
* @since 3.7.0
*/
final class Contact extends CMSPlugin
{
/**
* Load the language file on instantiation.
*
* @var boolean
* @since 3.7.0
*/
protected $autoloadLanguage = true;
/**
* Display the button
*
* @param string $name The name of the button to add
*
* @return CMSObject|void The button options as CMSObject
*
* @since 3.7.0
*/
public function onDisplay($name)
{
$user = $this->getApplication()->getIdentity();
if (
$user->authorise('core.create', 'com_contact')
|| $user->authorise('core.edit', 'com_contact')
|| $user->authorise('core.edit.own', 'com_contact')
) {
// The URL for the contacts list
$link = 'index.php?option=com_contact&view=contacts&layout=modal&tmpl=component&'
. Session::getFormToken() . '=1&editor=' . $name;
$button = new CMSObject();
$button->action = 'modal';
$button->link = $link;
$button->text = Text::_('PLG_EDITORS-XTD_CONTACT_BUTTON_CONTACT');
$button->name = $this->_type . '_' . $this->_name;
$button->icon = 'address';
$button->iconSVG = '<svg viewBox="0 0 448 512" width="24" height="24"><path d="M436 160c6.6 0 12-5.4 12-12v-40c0-6.6-5.4-12-12-12h-20V48c'
. '0-26.5-21.5-48-48-48H48C21.5 0 0 21.5 0 48v416c0 26.5 21.5 48 48 48h320c26.5 0 48-21.5 48-48v-48h20c6.6 0 12-5.4 1'
. '2-12v-40c0-6.6-5.4-12-12-12h-20v-64h20c6.6 0 12-5.4 12-12v-40c0-6.6-5.4-12-12-12h-20v-64h20zm-228-32c35.3 0 64 28.7'
. ' 64 64s-28.7 64-64 64-64-28.7-64-64 28.7-64 64-64zm112 236.8c0 10.6-10 19.2-22.4 19.2H118.4C106 384 96 375.4 96 364.'
. '8v-19.2c0-31.8 30.1-57.6 67.2-57.6h5c12.3 5.1 25.7 8 39.8 8s27.6-2.9 39.8-8h5c37.1 0 67.2 25.8 67.2 57.6v19.2z">'
. '</path></svg>';
return $button;
}
}
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="editors-xtd" method="upgrade">
<name>plg_editors-xtd_fields</name>
<author>Joomla! Project</author>
<creationDate>2017-02</creationDate>
<copyright>(C) 2017 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>3.7.0</version>
<description>PLG_EDITORS-XTD_FIELDS_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\EditorsXtd\Fields</namespace>
<files>
<folder plugin="fields">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_editors-xtd_fields.ini</language>
<language tag="en-GB">language/en-GB/plg_editors-xtd_fields.sys.ini</language>
</languages>
</extension>

View File

@ -0,0 +1,46 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Editors-xtd.fields
*
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\EditorsXtd\Fields\Extension\Fields;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.3.0
*/
public function register(Container $container)
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new Fields(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('editors-xtd', 'fields')
);
$plugin->setApplication(Factory::getApplication());
return $plugin;
}
);
}
};

View File

@ -0,0 +1,82 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Editors-xtd.fields
*
* @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Plugin\EditorsXtd\Fields\Extension;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Object\CMSObject;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Session\Session;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Editor Fields button
*
* @since 3.7.0
*/
final class Fields extends CMSPlugin
{
/**
* Load the language file on instantiation.
*
* @var boolean
* @since 3.7.0
*/
protected $autoloadLanguage = true;
/**
* Display the button
*
* @param string $name The name of the button to add
*
* @return CMSObject|void The button options as CMSObject
*
* @since 3.7.0
*/
public function onDisplay($name)
{
// Check if com_fields is enabled
if (!ComponentHelper::isEnabled('com_fields')) {
return;
}
// Guess the field context based on view.
$jinput = $this->getApplication()->getInput();
$context = $jinput->get('option') . '.' . $jinput->get('view');
// Special context for com_categories
if ($context === 'com_categories.category') {
$context = $jinput->get('extension', 'com_content') . '.categories';
}
$link = 'index.php?option=com_fields&view=fields&layout=modal&tmpl=component&context='
. $context . '&editor=' . $name . '&' . Session::getFormToken() . '=1';
$button = new CMSObject();
$button->action = 'modal';
$button->link = $link;
$button->text = Text::_('PLG_EDITORS-XTD_FIELDS_BUTTON_FIELD');
$button->name = $this->_type . '_' . $this->_name;
$button->icon = 'puzzle';
$button->iconSVG = '<svg viewBox="0 0 576 512" width="24" height="24"><path d="M519.442 288.651c-41.519 0-59.5 31.593-82.058 31.593C377.'
. '409 320.244 432 144 432 144s-196.288 80-196.288-3.297c0-35.827 36.288-46.25 36.288-85.985C272 19.216 243.885 0 210.'
. '539 0c-34.654 0-66.366 18.891-66.366 56.346 0 41.364 31.711 59.277 31.711 81.75C175.885 207.719 0 166.758 0 166.758'
. 'v333.237s178.635 41.047 178.635-28.662c0-22.473-40-40.107-40-81.471 0-37.456 29.25-56.346 63.577-56.346 33.673 0 61'
. '.788 19.216 61.788 54.717 0 39.735-36.288 50.158-36.288 85.985 0 60.803 129.675 25.73 181.23 25.73 0 0-34.725-120.1'
. '01 25.827-120.101 35.962 0 46.423 36.152 86.308 36.152C556.712 416 576 387.99 576 354.443c0-34.199-18.962-65.792-56'
. '.558-65.792z"></path></svg>';
return $button;
}
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="editors-xtd" method="upgrade">
<name>plg_editors-xtd_image</name>
<author>Joomla! Project</author>
<creationDate>2004-08</creationDate>
<copyright>(C) 2005 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>3.0.0</version>
<description>PLG_IMAGE_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\EditorsXtd\Image</namespace>
<files>
<folder plugin="image">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_editors-xtd_image.ini</language>
<language tag="en-GB">language/en-GB/plg_editors-xtd_image.sys.ini</language>
</languages>
</extension>

View File

@ -0,0 +1,46 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Editors-xtd.image
*
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\EditorsXtd\Image\Extension\Image;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.3.0
*/
public function register(Container $container)
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new Image(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('editors-xtd', 'image')
);
$plugin->setApplication(Factory::getApplication());
return $plugin;
}
);
}
};

View File

@ -0,0 +1,176 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Editors-xtd.image
*
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Plugin\EditorsXtd\Image\Extension;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Object\CMSObject;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Uri\Uri;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Editor Image button
*
* @since 1.5
*/
final class Image extends CMSPlugin
{
/**
* Load the language file on instantiation.
*
* @var boolean
* @since 3.1
*/
protected $autoloadLanguage = true;
/**
* Display the button.
*
* @param string $name The name of the button to display.
* @param string $asset The name of the asset being edited.
* @param integer $author The id of the author owning the asset being edited.
*
* @return CMSObject|false
*
* @since 1.5
*/
public function onDisplay($name, $asset, $author)
{
$doc = $this->getApplication()->getDocument();
$user = $this->getApplication()->getIdentity();
$extension = $this->getApplication()->getInput()->get('option');
// For categories we check the extension (ex: component.section)
if ($extension === 'com_categories') {
$parts = explode('.', $this->getApplication()->getInput()->get('extension', 'com_content'));
$extension = $parts[0];
}
$asset = $asset !== '' ? $asset : $extension;
if (
$user->authorise('core.edit', $asset)
|| $user->authorise('core.create', $asset)
|| (\count($user->getAuthorisedCategories($asset, 'core.create')) > 0)
|| ($user->authorise('core.edit.own', $asset) && $author === $user->id)
|| (\count($user->getAuthorisedCategories($extension, 'core.edit')) > 0)
|| (\count($user->getAuthorisedCategories($extension, 'core.edit.own')) > 0 && $author === $user->id)
) {
$doc->getWebAssetManager()
->useScript('webcomponent.media-select')
->useScript('webcomponent.field-media')
->useStyle('webcomponent.media-select');
$doc->addScriptOptions('xtdImageModal', [$name . '_ImageModal']);
$doc->addScriptOptions('media-picker-api', ['apiBaseUrl' => Uri::base() . 'index.php?option=com_media&format=json']);
if (\count($doc->getScriptOptions('media-picker')) === 0) {
$imagesExt = array_map(
'trim',
explode(
',',
ComponentHelper::getParams('com_media')->get(
'image_extensions',
'bmp,gif,jpg,jpeg,png,webp'
)
)
);
$audiosExt = array_map(
'trim',
explode(
',',
ComponentHelper::getParams('com_media')->get(
'audio_extensions',
'mp3,m4a,mp4a,ogg'
)
)
);
$videosExt = array_map(
'trim',
explode(
',',
ComponentHelper::getParams('com_media')->get(
'video_extensions',
'mp4,mp4v,mpeg,mov,webm'
)
)
);
$documentsExt = array_map(
'trim',
explode(
',',
ComponentHelper::getParams('com_media')->get(
'doc_extensions',
'doc,odg,odp,ods,odt,pdf,ppt,txt,xcf,xls,csv'
)
)
);
$doc->addScriptOptions('media-picker', [
'images' => $imagesExt,
'audios' => $audiosExt,
'videos' => $videosExt,
'documents' => $documentsExt,
]);
}
Text::script('JFIELD_MEDIA_LAZY_LABEL');
Text::script('JFIELD_MEDIA_ALT_LABEL');
Text::script('JFIELD_MEDIA_ALT_CHECK_LABEL');
Text::script('JFIELD_MEDIA_ALT_CHECK_DESC_LABEL');
Text::script('JFIELD_MEDIA_CLASS_LABEL');
Text::script('JFIELD_MEDIA_FIGURE_CLASS_LABEL');
Text::script('JFIELD_MEDIA_FIGURE_CAPTION_LABEL');
Text::script('JFIELD_MEDIA_LAZY_LABEL');
Text::script('JFIELD_MEDIA_SUMMARY_LABEL');
Text::script('JFIELD_MEDIA_EMBED_CHECK_DESC_LABEL');
Text::script('JFIELD_MEDIA_DOWNLOAD_CHECK_DESC_LABEL');
Text::script('JFIELD_MEDIA_DOWNLOAD_CHECK_LABEL');
Text::script('JFIELD_MEDIA_EMBED_CHECK_LABEL');
Text::script('JFIELD_MEDIA_WIDTH_LABEL');
Text::script('JFIELD_MEDIA_TITLE_LABEL');
Text::script('JFIELD_MEDIA_HEIGHT_LABEL');
Text::script('JFIELD_MEDIA_UNSUPPORTED');
Text::script('JFIELD_MEDIA_DOWNLOAD_FILE');
$link = 'index.php?option=com_media&view=media&tmpl=component&e_name=' . $name . '&asset=' . $asset . '&mediatypes=0,1,2,3' . '&author=' . $author;
$button = new CMSObject();
$button->modal = true;
$button->link = $link;
$button->text = Text::_('PLG_IMAGE_BUTTON_IMAGE');
$button->name = $this->_type . '_' . $this->_name;
$button->icon = 'pictures';
$button->iconSVG = '<svg width="24" height="24" viewBox="0 0 512 512"><path d="M464 64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48'
. ' 48 48h416c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm-6 336H54a6 6 0 0 1-6-6V118a6 6 0 0 1 6-6h404a6 6'
. ' 0 0 1 6 6v276a6 6 0 0 1-6 6zM128 152c-22.091 0-40 17.909-40 40s17.909 40 40 40 40-17.909 40-40-17.909-40-40-40'
. 'zM96 352h320v-80l-87.515-87.515c-4.686-4.686-12.284-4.686-16.971 0L192 304l-39.515-39.515c-4.686-4.686-12.284-4'
. '.686-16.971 0L96 304v48z"></path></svg>';
$button->options = [
'height' => '400px',
'width' => '800px',
'bodyHeight' => '70',
'modalWidth' => '80',
'tinyPath' => $link,
'confirmCallback' => 'Joomla.getImage(Joomla.selectedMediaFile, \'' . $name . '\', this)',
'confirmText' => Text::_('PLG_IMAGE_BUTTON_INSERT'),
];
return $button;
}
return false;
}
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="editors-xtd" method="upgrade">
<name>plg_editors-xtd_menu</name>
<author>Joomla! Project</author>
<creationDate>2016-08</creationDate>
<copyright>(C) 2016 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>3.7.0</version>
<description>PLG_EDITORS-XTD_MENU_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\EditorsXtd\Menu</namespace>
<files>
<folder plugin="menu">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_editors-xtd_menu.ini</language>
<language tag="en-GB">language/en-GB/plg_editors-xtd_menu.sys.ini</language>
</languages>
</extension>

View File

@ -0,0 +1,46 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Editors-xtd.article
*
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\EditorsXtd\Menu\Extension\Menu;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.3.0
*/
public function register(Container $container)
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new Menu(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('editors-xtd', 'menu')
);
$plugin->setApplication(Factory::getApplication());
return $plugin;
}
);
}
};

View File

@ -0,0 +1,76 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Editors-xtd.menu
*
* @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\Plugin\EditorsXtd\Menu\Extension;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Object\CMSObject;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Session\Session;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Editor menu button
*
* @since 3.7.0
*/
final class Menu extends CMSPlugin
{
/**
* Load the language file on instantiation.
*
* @var boolean
* @since 3.7.0
*/
protected $autoloadLanguage = true;
/**
* Display the button
*
* @param string $name The name of the button to add
*
* @since 3.7.0
* @return CMSObject
*/
public function onDisplay($name)
{
/*
* Use the built-in element view to select the menu item.
* Currently uses blank class.
*/
$user = $this->getApplication()->getIdentity();
if (
$user->authorise('core.create', 'com_menus')
|| $user->authorise('core.edit', 'com_menus')
) {
$link = 'index.php?option=com_menus&view=items&layout=modal&tmpl=component&'
. Session::getFormToken() . '=1&editor=' . $name;
$button = new CMSObject();
$button->action = 'modal';
$button->link = $link;
$button->text = Text::_('PLG_EDITORS-XTD_MENU_BUTTON_MENU');
$button->name = $this->_type . '_' . $this->_name;
$button->icon = 'list';
$button->iconSVG = '<svg viewBox="0 0 512 512" width="24" height="24"><path d="M80 368H16a16 16 0 0 0-16 16v64a16 16 0 0 0 16 16h64a16 1'
. '6 0 0 0 16-16v-64a16 16 0 0 0-16-16zm0-320H16A16 16 0 0 0 0 64v64a16 16 0 0 0 16 16h64a16 16 0 0 0 16-16V64a16 16 '
. '0 0 0-16-16zm0 160H16a16 16 0 0 0-16 16v64a16 16 0 0 0 16 16h64a16 16 0 0 0 16-16v-64a16 16 0 0 0-16-16zm416 176H1'
. '76a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-320H176a16 16 0 0 0-16 16'
. 'v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16'
. 'h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16z"></path></svg>';
return $button;
}
}
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="editors-xtd" method="upgrade">
<name>plg_editors-xtd_module</name>
<author>Joomla! Project</author>
<creationDate>2015-10</creationDate>
<copyright>(C) 2015 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>3.5.0</version>
<description>PLG_MODULE_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\EditorsXtd\Module</namespace>
<files>
<folder plugin="module">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_editors-xtd_module.ini</language>
<language tag="en-GB">language/en-GB/plg_editors-xtd_module.sys.ini</language>
</languages>
</extension>

View File

@ -0,0 +1,46 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Editors-xtd.article
*
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\EditorsXtd\Module\Extension\Module;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.3.0
*/
public function register(Container $container)
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new Module(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('editors-xtd', 'module')
);
$plugin->setApplication(Factory::getApplication());
return $plugin;
}
);
}
};

View File

@ -0,0 +1,76 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Editors-xtd.module
*
* @copyright (C) 2015 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Plugin\EditorsXtd\Module\Extension;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Object\CMSObject;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Session\Session;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Editor Module button
*
* @since 3.5
*/
final class Module extends CMSPlugin
{
/**
* Load the language file on instantiation.
*
* @var boolean
* @since 3.5
*/
protected $autoloadLanguage = true;
/**
* Display the button
*
* @param string $name The name of the button to add
*
* @return CMSObject|void The button options as CMSObject
*
* @since 3.5
*/
public function onDisplay($name)
{
/*
* Use the built-in element view to select the module.
* Currently uses blank class.
*/
$user = $this->getApplication()->getIdentity();
if (
$user->authorise('core.create', 'com_modules')
|| $user->authorise('core.edit', 'com_modules')
|| $user->authorise('core.edit.own', 'com_modules')
) {
$link = 'index.php?option=com_modules&view=modules&layout=modal&tmpl=component&'
. Session::getFormToken() . '=1&editor=' . $name;
$button = new CMSObject();
$button->action = 'modal';
$button->link = $link;
$button->text = Text::_('PLG_MODULE_BUTTON_MODULE');
$button->name = $this->_type . '_' . $this->_name;
$button->icon = 'cube';
$button->iconSVG = '<svg viewBox="0 0 512 512" width="24" height="24"><path d="M239.1 6.3l-208 78c-18.7 7-31.1 '
. '25-31.1 45v225.1c0 18.2 10.3 34.8 26.5 42.9l208 104c13.5 6.8 29.4 6.8 42.9 0l208-104c16.3-8.1 26.5-24.8 '
. '26.5-42.9V129.3c0-20-12.4-37.9-31.1-44.9l-208-78C262 2.2 250 2.2 239.1 6.3zM256 68.4l192 72v1.1l-192 '
. '78-192-78v-1.1l192-72zm32 356V275.5l160-65v133.9l-160 80z"></path></svg>';
return $button;
}
}
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="editors-xtd" method="upgrade">
<name>plg_editors-xtd_pagebreak</name>
<author>Joomla! Project</author>
<creationDate>2004-08</creationDate>
<copyright>(C) 2005 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>3.0.0</version>
<description>PLG_EDITORSXTD_PAGEBREAK_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\EditorsXtd\PageBreak</namespace>
<files>
<folder plugin="pagebreak">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_editors-xtd_pagebreak.ini</language>
<language tag="en-GB">language/en-GB/plg_editors-xtd_pagebreak.sys.ini</language>
</languages>
</extension>

View File

@ -0,0 +1,46 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Editors-xtd.pagebreak
*
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\EditorsXtd\PageBreak\Extension\PageBreak;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.4.0
*/
public function register(Container $container)
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new PageBreak(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('editors-xtd', 'pagebreak')
);
$plugin->setApplication(Factory::getApplication());
return $plugin;
}
);
}
};

View File

@ -0,0 +1,84 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Editors-xtd.pagebreak
*
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Plugin\EditorsXtd\PageBreak\Extension;
use Joomla\CMS\Application\CMSWebApplicationInterface;
use Joomla\CMS\Object\CMSObject;
use Joomla\CMS\Plugin\CMSPlugin;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Editor Pagebreak button
*
* @since 1.5
*/
final class PageBreak extends CMSPlugin
{
/**
* Load the language file on instantiation.
*
* @var boolean
* @since 3.1
*/
protected $autoloadLanguage = true;
/**
* Display the button
*
* @param string $name The name of the button to add
*
* @return CMSObject|void The button options as CMSObject
*
* @since 1.5
*/
public function onDisplay($name)
{
$app = $this->getApplication();
if (!$app instanceof CMSWebApplicationInterface) {
return;
}
$user = $app->getIdentity();
// Can create in any category (component permission) or at least in one category
$canCreateRecords = $user->authorise('core.create', 'com_content')
|| \count($user->getAuthorisedCategories('com_content', 'core.create')) > 0;
// Instead of checking edit on all records, we can use **same** check as the form editing view
$values = (array) $app->getUserState('com_content.edit.article.id');
$isEditingRecords = \count($values);
// This ACL check is probably a double-check (form view already performed checks)
$hasAccess = $canCreateRecords || $isEditingRecords;
if (!$hasAccess) {
return;
}
$app->getDocument()->addScriptOptions('xtd-pagebreak', ['editor' => $name]);
$link = 'index.php?option=com_content&amp;view=article&amp;layout=pagebreak&amp;tmpl=component&amp;e_name=' . $name;
$button = new CMSObject();
$button->action = 'modal';
$button->link = $link;
$button->text = $app->getLanguage()->_('PLG_EDITORSXTD_PAGEBREAK_BUTTON_PAGEBREAK');
$button->name = $this->_type . '_' . $this->_name;
$button->icon = 'copy';
$button->iconSVG = '<svg viewBox="0 0 32 32" width="24" height="24"><path d="M26 8h-6v-2l-6-6h-14v24h12v8h20v-18l-6-6zM26 10.828l3.172 3'
. '.172h-3.172v-3.172zM14 2.828l3.172 3.172h-3.172v-3.172zM2 2h10v6h6v14h-16v-20zM30 30h-16v-6h6v-14h4v6h6v14z"></pa'
. 'th></svg>';
return $button;
}
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="editors-xtd" method="upgrade">
<name>plg_editors-xtd_readmore</name>
<author>Joomla! Project</author>
<creationDate>2006-03</creationDate>
<copyright>(C) 2006 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>3.0.0</version>
<description>PLG_READMORE_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\EditorsXtd\ReadMore</namespace>
<files>
<folder plugin="readmore">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_editors-xtd_readmore.ini</language>
<language tag="en-GB">language/en-GB/plg_editors-xtd_readmore.sys.ini</language>
</languages>
</extension>

View File

@ -0,0 +1,46 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Editors-xtd.article
*
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\EditorsXtd\ReadMore\Extension\ReadMore;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.3.0
*/
public function register(Container $container)
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new ReadMore(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('editors-xtd', 'readmore')
);
$plugin->setApplication(Factory::getApplication());
return $plugin;
}
);
}
};

View File

@ -0,0 +1,101 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Editors-xtd.readmore
*
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Plugin\EditorsXtd\ReadMore\Extension;
use Joomla\CMS\Editor\Button\Button;
use Joomla\CMS\Event\Editor\EditorButtonsSetupEvent;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\SubscriberInterface;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Editor Readmore button
*
* @since 1.5
*/
final class ReadMore extends CMSPlugin implements SubscriberInterface
{
/**
* Returns an array of events this subscriber will listen to.
*
* @return array
*
* @since 5.0.0
*/
public static function getSubscribedEvents(): array
{
return ['onEditorButtonsSetup' => 'onEditorButtonsSetup'];
}
/**
* @param EditorButtonsSetupEvent $event
* @return void
*
* @since 5.0.0
*/
public function onEditorButtonsSetup(EditorButtonsSetupEvent $event)
{
$subject = $event->getButtonsRegistry();
$disabled = $event->getDisabledButtons();
if (\in_array($this->_name, $disabled)) {
return;
}
$this->loadLanguage();
$button = $this->onDisplay($event->getEditorId());
$subject->add($button);
}
/**
* Readmore button
*
* @param string $name The name of the button to add
*
* @return Button $button A two element array of (imageName, textToInsert)
*
* @since 1.5
*
* @deprecated 6.0 Use onEditorButtonsSetup event
*/
public function onDisplay($name)
{
// Register the asset "editor-button.<button name>", will be loaded by the button layout
$this->getApplication()->getDocument()->getWebAssetManager()
->registerScript(
'editor-button.' . $this->_name,
'com_content/admin-article-readmore.min.js',
[],
['type' => 'module'],
['editors', 'joomla.dialog']
);
Text::script('PLG_READMORE_ALREADY_EXISTS');
$button = new Button(
$this->_name,
[
'action' => 'insert-readmore',
'text' => Text::_('PLG_READMORE_BUTTON_READMORE'),
'icon' => 'arrow-down',
'iconSVG' => '<svg viewBox="0 0 32 32" width="24" height="24"><path d="M32 12l-6-6-10 10-10-10-6 6 16 16z"></path></svg>',
// This is whole Plugin name, it is needed for keeping backward compatibility
'name' => $this->_type . '_' . $this->_name,
]
);
return $button;
}
}

View File

@ -0,0 +1,168 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="editors" method="upgrade">
<name>plg_editors_codemirror</name>
<version>6.0.0</version>
<creationDate>28 March 2011</creationDate>
<author>Marijn Haverbeke</author>
<authorEmail>marijnh@gmail.com</authorEmail>
<authorUrl>https://codemirror.net/</authorUrl>
<copyright>Copyright (C) 2014 - 2021 by Marijn Haverbeke &lt;marijnh@gmail.com&gt; and others</copyright>
<license>MIT license: https://codemirror.net/LICENSE</license>
<description>PLG_CODEMIRROR_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\Editors\CodeMirror</namespace>
<files>
<folder>layouts</folder>
<folder plugin="codemirror">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_editors_codemirror.ini</language>
<language tag="en-GB">language/en-GB/plg_editors_codemirror.sys.ini</language>
</languages>
<config>
<fields name="params">
<fieldset name="basic">
<field
name="lineNumbers"
type="radio"
layout="joomla.form.field.radio.switcher"
label="PLG_CODEMIRROR_FIELD_LINENUMBERS_LABEL"
default="1"
filter="integer"
>
<option value="0">JOFF</option>
<option value="1">JON</option>
</field>
<field
name="codeFolding"
type="radio"
label="PLG_CODEMIRROR_FIELD_CODEFOLDING_LABEL"
layout="joomla.form.field.radio.switcher"
default="1"
filter="integer"
>
<option value="0">JOFF</option>
<option value="1">JON</option>
</field>
<field
name="lineWrapping"
type="radio"
label="PLG_CODEMIRROR_FIELD_LINEWRAPPING_LABEL"
layout="joomla.form.field.radio.switcher"
default="1"
filter="integer"
>
<option value="0">JOFF</option>
<option value="1">JON</option>
</field>
<field
name="activeLine"
type="radio"
label="PLG_CODEMIRROR_FIELD_ACTIVELINE_LABEL"
layout="joomla.form.field.radio.switcher"
default="1"
filter="integer"
>
<option value="0">JOFF</option>
<option value="1">JON</option>
</field>
<field
name="selectionMatches"
type="radio"
layout="joomla.form.field.radio.switcher"
label="PLG_CODEMIRROR_FIELD_SELECTIONMATCHES_LABEL"
default="1"
filter="integer"
>
<option value="0">JOFF</option>
<option value="1">JON</option>
</field>
<field
name="autoCloseBrackets"
type="radio"
layout="joomla.form.field.radio.switcher"
label="PLG_CODEMIRROR_FIELD_AUTOCLOSEBRACKET_LABEL"
default="1"
filter="integer"
>
<option value="0">JOFF</option>
<option value="1">JON</option>
</field>
<field
name="keyMap"
type="list"
label="PLG_CODEMIRROR_FIELD_KEYMAP_LABEL"
description="PLG_CODEMIRROR_FIELD_KEYMAP_DESC"
default=""
validate="options"
>
<option value="">JDEFAULT</option>
<option value="emacs">PLG_CODEMIRROR_FIELD_KEYMAP_EMACS</option>
</field>
<field
name="fullScreen"
type="list"
label="PLG_CODEMIRROR_FIELD_FULLSCREEN_LABEL"
default="F10"
validate="options"
>
<option value="F1">F1</option>
<option value="F2">F2</option>
<option value="F3">F3</option>
<option value="F4">F4</option>
<option value="F5">F5</option>
<option value="F6">F6</option>
<option value="F7">F7</option>
<option value="F8">F8</option>
<option value="F9">F9</option>
<option value="F10">F10</option>
<option value="F11">F11</option>
<option value="F12">F12</option>
</field>
<field
name="fullScreenMod"
type="checkboxes"
label="PLG_CODEMIRROR_FIELD_FULLSCREEN_MOD_LABEL"
>
<option value="Shift">PLG_CODEMIRROR_FIELD_VALUE_FULLSCREEN_MOD_SHIFT</option>
<option value="Cmd">PLG_CODEMIRROR_FIELD_VALUE_FULLSCREEN_MOD_CMD</option>
<option value="Ctrl">PLG_CODEMIRROR_FIELD_VALUE_FULLSCREEN_MOD_CTRL</option>
<option value="Alt">PLG_CODEMIRROR_FIELD_VALUE_FULLSCREEN_MOD_ALT</option>
</field>
</fieldset>
<fieldset name="advanced">
<field
name="customExtensions"
type="subform"
label="PLG_CODEMIRROR_FIELD_CUSTOM_EXTENSIONS_LABEL"
multiple="true"
>
<form>
<field
name="module"
type="text"
label="PLG_CODEMIRROR_FIELD_CUSTOM_EXTENSIONS_MODULE_LABEL"
description="PLG_CODEMIRROR_FIELD_CUSTOM_EXTENSIONS_MODULE_DESCR"
/>
<field
name="methods"
type="text"
label="PLG_CODEMIRROR_FIELD_CUSTOM_EXTENSIONS_METHOD_LABEL"
description="PLG_CODEMIRROR_FIELD_CUSTOM_EXTENSIONS_METHOD_DESCR"
/>
</form>
</field>
</fieldset>
</fields>
</config>
</extension>

View File

@ -0,0 +1,66 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Editors.codemirror
*
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
// No direct access
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\Registry\Registry;
extract($displayData);
/**
* Layout variables
*
* @var array $options JS options for editor
* @var Registry $params Plugin parameters
* @var string $id The id of the input
* @var string $name The name of the input
* @var integer $cols Textarea cols attribute
* @var integer $rows Textarea rows attribute
* @var string $content The value
* @var string $buttons Editor XTD buttons
*/
$option = ' options="' . $this->escape(json_encode($options)) . '"';
$style = '';
if ($options->width) {
$style .= 'width:' . $options->width . ';';
}
if ($options->height) {
$style .= 'height:' . $options->height . ';';
}
// Fullscreen combo
$fsCombo = '';
if (empty($options->readOnly)) {
$fskeys = $params->get('fullScreenMod', []);
$fskeys[] = $params->get('fullScreen', 'F10');
$fullScreenCombo = implode('-', $fskeys);
$fsCombo = ' fs-combo=' . $this->escape($fullScreenCombo);
}
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();
$wa->getRegistry()->addExtensionRegistryFile('plg_editors_codemirror');
$wa->useStyle('plg_editors_codemirror')
->useScript('webcomponent.editor-codemirror');
?>
<joomla-editor-codemirror <?php echo $fsCombo . $option; ?>>
<?php echo '<textarea name="' . $name . '" id="' . $id . '" cols="' . $cols . '" rows="' . $rows . '" style="' . $style . '">' . $content . '</textarea>'; ?>
<?php if ($fsCombo) : ?>
<p class="small float-end">
<?php echo Text::sprintf('PLG_CODEMIRROR_TOGGLE_FULL_SCREEN', $fullScreenCombo); ?>
</p>
<?php endif; ?>
<?php echo $buttons ?? ''; ?>
</joomla-editor-codemirror>

View File

@ -0,0 +1,46 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Editors.codemirror
*
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Editors\CodeMirror\Extension\Codemirror;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.3.0
*/
public function register(Container $container)
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new Codemirror(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('editors', 'codemirror')
);
$plugin->setApplication(Factory::getApplication());
return $plugin;
}
);
}
};

View File

@ -0,0 +1,57 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Editors.codemirror
*
* @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Plugin\Editors\CodeMirror\Extension;
use Joomla\CMS\Event\Editor\EditorSetupEvent;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\SubscriberInterface;
use Joomla\Plugin\Editors\CodeMirror\Provider\CodeMirrorProvider;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* CodeMirror Editor Plugin.
*
* @since 1.6
*/
final class Codemirror extends CMSPlugin implements SubscriberInterface
{
/**
* Returns an array of events this subscriber will listen to.
*
* @return array
*
* @since 5.0.0
*/
public static function getSubscribedEvents(): array
{
return ['onEditorSetup' => 'onEditorSetup'];
}
/**
* Register Editor instance
*
* @param EditorSetupEvent $event
*
* @return void
*
* @since 5.0.0
*/
public function onEditorSetup(EditorSetupEvent $event)
{
$this->loadLanguage();
$event->getEditorsRegistry()
->add(new CodeMirrorProvider($this->params, $this->getApplication(), $this->getDispatcher()));
}
}

View File

@ -0,0 +1,165 @@
<?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\Plugin\Editors\CodeMirror\Provider;
use Joomla\CMS\Application\CMSApplicationInterface;
use Joomla\CMS\Editor\AbstractEditorProvider;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\Uri\Uri;
use Joomla\Event\DispatcherInterface;
use Joomla\Registry\Registry;
/**
* Editor provider class
*
* @since 5.0.0
*/
final class CodeMirrorProvider extends AbstractEditorProvider
{
/**
* A Registry object holding the parameters for the plugin
*
* @var Registry
* @since 5.0.0
*/
protected $params;
/**
* The application object
*
* @var CMSApplicationInterface
*
* @since 5.0.0
*/
protected $application;
/**
* Class constructor
*
* @param Registry $params
* @param CMSApplicationInterface $application
* @param DispatcherInterface $dispatcher
*
* @since 5.0.0
*/
public function __construct(Registry $params, CMSApplicationInterface $application, DispatcherInterface $dispatcher)
{
$this->params = $params;
$this->application = $application;
$this->setDispatcher($dispatcher);
}
/**
* Return Editor name, CMD string.
*
* @return string
* @since 5.0.0
*/
public function getName(): string
{
return 'codemirror';
}
/**
* Gets the editor HTML markup
*
* @param string $name Input name.
* @param string $content The content of the field.
* @param array $attributes Associative array of editor attributes.
* @param array $params Associative array of editor parameters.
*
* @return string The HTML markup of the editor
*
* @since 5.0.0
*/
public function display(string $name, string $content = '', array $attributes = [], array $params = []): string
{
$col = $attributes['col'] ?? '';
$row = $attributes['row'] ?? '';
$width = $attributes['width'] ?? '';
$height = $attributes['height'] ?? '';
$id = $attributes['id'] ?? '';
$buttons = $params['buttons'] ?? true;
$asset = $params['asset'] ?? 0;
$author = $params['author'] ?? 0;
// Must pass the field id to the buttons in this editor.
$buttonsStr = $this->displayButtons($buttons, ['asset' => $asset, 'author' => $author, 'editorId' => $id]);
// Options for the CodeMirror constructor.
$options = new \stdClass();
// Is field readonly?
if (!empty($params['readonly'])) {
$options->readOnly = true;
}
// Only add "px" to width and height if they are not given as a percentage.
$options->width = is_numeric($width) ? $width . 'px' : $width;
$options->height = is_numeric($height) ? $height . 'px' : $height;
$options->lineNumbers = (bool) $this->params->get('lineNumbers', 1);
$options->foldGutter = (bool) $this->params->get('codeFolding', 1);
$options->lineWrapping = (bool) $this->params->get('lineWrapping', 1);
$options->activeLine = (bool) $this->params->get('activeLine', 1);
$options->highlightSelection = (bool) $this->params->get('selectionMatches', 1);
// Load the syntax mode.
$modeAlias = [
'scss' => 'css',
'sass' => 'css',
'less' => 'css',
];
$options->mode = !empty($params['syntax']) ? $params['syntax'] : $this->params->get('syntax', 'html');
$options->mode = $modeAlias[$options->mode] ?? $options->mode;
// Special options for non-tagged modes.
if (!\in_array($options->mode, ['xml', 'html'])) {
// Autogenerate closing brackets.
$options->autoCloseBrackets = (bool) $this->params->get('autoCloseBrackets', 1);
}
// KeyMap settings.
$options->keyMap = $this->params->get('keyMap', '');
// Check for custom extensions
$customExtensions = $this->params->get('customExtensions', []);
$options->customExtensions = [];
if ($customExtensions) {
foreach ($customExtensions as $item) {
$methods = array_filter(array_map('trim', explode(',', $item->methods ?? '')));
if (empty($item->module) || !$methods) {
continue;
}
// Prepend root path if we have a file
$module = str_ends_with($item->module, '.js') ? Uri::root(true) . '/' . $item->module : $item->module;
$options->customExtensions[] = [$module, $methods];
}
}
$displayData = [
'options' => $options,
'params' => $this->params,
'name' => $name,
'id' => $id,
'cols' => $col,
'rows' => $row,
'content' => $content,
'buttons' => $buttonsStr,
];
return LayoutHelper::render('editors.codemirror.codemirror', $displayData, JPATH_PLUGINS . '/editors/codemirror/layouts');
}
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="editors" method="upgrade">
<name>plg_editors_none</name>
<version>3.0.0</version>
<creationDate>2005-09</creationDate>
<author>Joomla! Project</author>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<copyright>(C) 2005 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<description>PLG_NONE_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\Editors\None</namespace>
<files>
<folder plugin="none">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_editors_none.ini</language>
<language tag="en-GB">language/en-GB/plg_editors_none.sys.ini</language>
</languages>
</extension>

View File

@ -0,0 +1,46 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Editors.none
*
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Editors\None\Extension\None;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.3.0
*/
public function register(Container $container)
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new None(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('editors', 'none')
);
$plugin->setApplication(Factory::getApplication());
return $plugin;
}
);
}
};

View File

@ -0,0 +1,116 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Editors.none
*
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Plugin\Editors\None\Extension;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\Event;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Plain Textarea Editor Plugin
*
* @since 1.5
*/
final class None extends CMSPlugin
{
/**
* Display the editor area.
*
* @param string $name The control name.
* @param string $content The contents of the text area.
* @param string $width The width of the text area (px or %).
* @param string $height The height of the text area (px or %).
* @param integer $col The number of columns for the textarea.
* @param integer $row The number of rows for the textarea.
* @param boolean $buttons True and the editor buttons will be displayed.
* @param string $id An optional ID for the textarea (note: since 1.6). If not supplied the name is used.
* @param string $asset The object asset
* @param object $author The author.
* @param array $params Associative array of editor parameters.
*
* @return string
*/
public function onDisplay(
$name,
$content,
$width,
$height,
$col,
$row,
$buttons = true,
$id = null,
$asset = null,
$author = null,
$params = []
) {
if (empty($id)) {
$id = $name;
}
// Only add "px" to width and height if they are not given as a percentage
if (is_numeric($width)) {
$width .= 'px';
}
if (is_numeric($height)) {
$height .= 'px';
}
$readonly = !empty($params['readonly']) ? ' readonly disabled' : '';
$this->getApplication()->getDocument()->getWebAssetManager()
->registerAndUseScript(
'webcomponent.editor-none',
'plg_editors_none/joomla-editor-none.min.js',
[],
['type' => 'module'],
['editors']
);
return '<joomla-editor-none>'
. '<textarea name="' . $name . '" id="' . $id . '" cols="' . $col . '" rows="' . $row
. '" style="width: ' . $width . '; height: ' . $height . ';"' . $readonly . '>' . $content . '</textarea>'
. $this->displayButtons($id, $buttons, $asset, $author)
. '</joomla-editor-none>';
}
/**
* Displays the editor buttons.
*
* @param string $name The control name.
* @param mixed $buttons [array with button objects | boolean true to display buttons]
* @param string $asset The object asset
* @param object $author The author.
*
* @return void|string HTML
*/
private function displayButtons($name, $buttons, $asset, $author)
{
if (\is_array($buttons) || (\is_bool($buttons) && $buttons)) {
$buttonsEvent = new Event(
'getButtons',
[
'editor' => $name,
'buttons' => $buttons,
]
);
$buttonsResult = $this->getDispatcher()->dispatch('getButtons', $buttonsEvent);
$buttons = $buttonsResult['result'];
return LayoutHelper::render('joomla.editors.buttons', $buttons);
}
}
}

Some files were not shown because too many files have changed in this diff Show More