first commit

This commit is contained in:
2026-02-08 21:16:11 +01:00
commit e17b7026fd
8881 changed files with 1160453 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
<?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>4.4.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>
</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,41 @@
<?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\Plugin\CMSPlugin;
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
{
/**
* This plugin does not listen to any events.
*/
return [];
}
}

View File

@@ -0,0 +1,45 @@
<?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) {
$dispatcher = $container->get(DispatcherInterface::class);
$plugin = new Taggable(
$dispatcher,
(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,51 @@
<?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) {
$dispatcher = $container->get(DispatcherInterface::class);
$plugin = new Versionable(
$dispatcher,
(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>