From 1efb121f5dd9daf437372daf81e9f38a5f4d834c Mon Sep 17 00:00:00 2001 From: Robbie Jackson Date: Mon, 8 Dec 2025 11:12:09 +0000 Subject: [PATCH 1/5] replacement of joomla 4 and 5 changes page --- .../plugins/_assets/plg_shortcodes.zip | Bin 3998 -> 4097 bytes .../plugins/_assets/shortcodes.jpg | Bin 17135 -> 0 bytes .../plugins/advanced-features.md | 32 ++ .../plugins/basic-content-plugin.md | 101 +++--- .../plugins/how-plugins-work.md | 14 + .../plugins/joomla-4-and-5-changes.md | 284 ----------------- .../plugins/methods-and-arguments.md | 298 ++++++++++++++++++ .../plugins/plugin-events/content.md | 2 +- .../plugins/plugin-events/index.md | 2 - .../plugins/plugin-events/installer.md | 4 +- .../plugins/plugin-examples/ajax-plugin.md | 10 +- 11 files changed, 402 insertions(+), 345 deletions(-) delete mode 100644 docs/building-extensions/plugins/_assets/shortcodes.jpg create mode 100644 docs/building-extensions/plugins/advanced-features.md delete mode 100644 docs/building-extensions/plugins/joomla-4-and-5-changes.md create mode 100644 docs/building-extensions/plugins/methods-and-arguments.md diff --git a/docs/building-extensions/plugins/_assets/plg_shortcodes.zip b/docs/building-extensions/plugins/_assets/plg_shortcodes.zip index 1950497b9cdae9d6953bfe6fcf14e219e4d40e8f..5852e24e59b42cd7dc5aebf78a4319fde555ae3b 100644 GIT binary patch delta 2224 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
    >8fL3O3$IJ>=|IsYw09VKi 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 ['myContentPrepareMethod', \Joomla\Event\Priority::HIGH], + 'onContentAfterTitle' => ['myContentAfterTitleMethod', \Joomla\Event\Priority::MIN], + ]; +} +``` + +See the other values available in libraries/vendor/joomla/event/src/Priority.php. +Use with caution though, as this will override any priority which the site administrator may want to set through ordering the plugins. + +## Stop Propagation + +To stop the propagation of an event to other plugins you can call + +```php +$event->stopPropagation(); +``` diff --git a/docs/building-extensions/plugins/basic-content-plugin.md b/docs/building-extensions/plugins/basic-content-plugin.md index b443ad34..6c7bee0c 100644 --- a/docs/building-extensions/plugins/basic-content-plugin.md +++ b/docs/building-extensions/plugins/basic-content-plugin.md @@ -19,14 +19,24 @@ In addition, the plugin demonstrates the use of: - language constants - both in the manifest file and in the plugin code - returning a value from a plugin method - through the use of the `onContentAfterTitle` event. The plugin code adds some text after the article title. -You can test this plugin on both Joomla 4 and Joomla 5 instances, to see the differences in obtaining the parameters and returning the result (as described in [Joomla 4 and 5 changes](joomla-4-and-5-changes.md)). +The diagram below the plugin files to write, or you can download a zip file of the plugin from [shortcodes plugin download](_assets/plg_shortcodes.zip). -![Shortcodes plugin files](_assets/shortcodes.jpg "Shortcodes plugin files") - -The diagram shows the plugin files to write, or you can download a zip file of the plugin from [shortcodes plugin download](_assets/plg_shortcodes.zip). +``` +plg_shortcodes + ├─── language + │ └─── en-GB + │ ├─── plg_content_shortcodes.ini + │ └─── plg_content_shortcodes.sys.ini + ├─── services + │ └─── provider.php + ├─── src + │ └─── Extension + │ └─── Shortcode.php + └─── shortcodes.xml +``` ## Manifest File -For general information on manifest files see [Manifest Files](https://docs.joomla.org/Manifest_files). +For general information on manifest files see [Manifest Files](../install-update/installation/manifest.md). ```xml title="plg_shortcodes/shortcodes.xml" @@ -36,7 +46,7 @@ For general information on manifest files see [Manifest Files](https://docs.joom PLG_CONTENT_SHORTCODES_DESCRIPTION Me Today - (C) 2024 Open Source Matters, Inc. + (C) 2025 Open Source Matters, Inc. GNU General Public License version 2 or later My\Plugin\Content\Shortcodes @@ -58,7 +68,7 @@ Joomla is quite particular when it comes to plugin manifest files, and it's easy ``` -Previous sections described plugin types as 'content', 'system', etc but here the `type` is "plugin" (as it's the type of the extension) and the `group` refers to the plugin type. +Here the `type` is "plugin" (as it's the type of the extension) and the `group` refers to the plugin type. ### Language constants @@ -156,7 +166,7 @@ This is pretty much boilerplate code for obtaining your plugin from the Dependen use My\Plugin\Content\Shortcodes\Extension\Shortcode; ``` -Ensure that this aligns with your `` in the manifest file and your `namespace` statement and class name in the Extension class file. . +Ensure that this aligns with your `` in the manifest file and your `namespace` statement and class name in the Extension class file. ```php $config = (array) PluginHelper::getPlugin('content', 'shortcodes'); @@ -172,16 +182,7 @@ Ensure that this matches your class in your `src/Extension` directory. ### Extension Class This is the main code of the plugin. Hopefully the comments in the code explain what is going on. - -As explained in [Joomla 4 and 5 changes](./joomla-4-and-5-changes.md), code which triggers the Events can use a `GenericEvent` or a concrete Event, eg `ContentPrepareEvent`. In both these cases you can get the arguments using - -```php -[$context, $article, $params, $page] = array_values($event->getArguments()); -``` - -but you have to check in your code how to return the result. - -Using this approach means that you don't need to change your plugin code if the code which is triggering the event changes from using a generic Event class to a concrete event class. +For further details see the page on [Plugin Methods and Arguments](./methods-and-arguments.md). ```php title="plg_shortcodes/src/Extension/Shortcode.php" getApplication(); + + // The line below restricts the functionality to the front-end site (ie not admin/api/console job) // You may not want this, so you need to consider this in your own plugins - if (!$this->getApplication()->isClient('site')) { + if (!$app->isClient('site')) { return; } - - // use this format to get the arguments for both Joomla 4 and Joomla 5 - // In Joomla 4 a generic Event is passed - // In Joomla 5 a concrete ContentPrepareEvent is passed - [$context, $article, $params, $page] = array_values($event->getArguments()); + + // Find out if this event relates to an article or something else (eg contact, user) + $context = $event->getContext(); if ($context !== "com_content.article" && $context !== "com_content.featured") return; + // If we've reached here then it's an article - get the 'text' property + $article = $event->getItem(); $text = $article->text; // text of the article - $config = Factory::getApplication()->getConfig()->toArray(); // config params as an array + + $config = $app->getConfig()->toArray(); // config params as an array // (we can't do a foreach over the config params as a Registry because they're protected) // the following is just code to replace {configname} with the parameter value @@ -269,22 +273,21 @@ class Shortcode extends CMSPlugin implements SubscriberInterface $article->text = $text; } - public function addShortcodeSubtitle(Event $event) + public function addShortcodeSubtitle(AfterTitleEvent $event): void { - if (!$this->getApplication()->isClient('site')) { - return; - } - [$context, $article, $params, $page] = array_values($event->getArguments()); - if ($context !== "com_content.article" && $context !== "com_content.featured") return; - - $eventType = method_exists($event, 'getContext') ? "concrete event class" : "generic event class"; + /* This function adds a subtitle to a page on the site front end: + * "Processed for shortcodes" - if the page is an article + * "Not processed for shortcodes" - if the page is a contact, etc. + */ + + if (!$this->getApplication()->isClient('site')) return; - if ($event instanceof ResultAwareInterface) { - $event->addResult("{$eventType} via addResult"); + $context = $event->getContext(); + if ($context === "com_content.article" || $context === "com_content.featured") + { + $event->addResult(Text::_('PLG_CONTENT_SHORTCODES_PROCESSED')); } else { - $result = $event->getArgument('result') ?? []; - $result[] = "{$eventType} via setArgument"; - $event->setArgument('result', $result); + $event->addResult(Text::_('PLG_CONTENT_SHORTCODES_NOT_PROCESSED')); } } } @@ -302,6 +305,8 @@ Language constants which are used in the plugin code: ```php title="plg_shortcodes/language/en-GB/plg_content_shortcodes.ini" PLG_CONTENT_SHORTCODES_NO_MATCH="Error: no match for shortcode found" +PLG_CONTENT_SHORTCODES_PROCESSED="Processed for shortcodes" +PLG_CONTENT_SHORTCODES_NOT_PROCESSED="Not processed for shortcodes" ``` ## Installation @@ -313,4 +318,6 @@ On my Joomla instance {sitename} the default editor is {editor}. ``` Then navigate to a Joomla site menuitem which displays this article, either as a single article or as one of the featured articles. -The code should work on both Joomla 4 and Joomla 5, and should also display after the article title information about the type of event class used, and how the value is returned. +Include a non-existent configuration parameter in your shortcode to display the error text. + +Also compare the subtitle shown for a page displaying an article with a page displaying a contact. \ No newline at end of file diff --git a/docs/building-extensions/plugins/how-plugins-work.md b/docs/building-extensions/plugins/how-plugins-work.md index 8c076941..27c8f159 100644 --- a/docs/building-extensions/plugins/how-plugins-work.md +++ b/docs/building-extensions/plugins/how-plugins-work.md @@ -2,6 +2,12 @@ title: How Plugins Work sidebar_position: 1 --- + +How Plugins Work +================ + +## Overview + ![Plugins Overview](_assets/plugin-overview.jpg "Plugins Overview") The diagram shows how Joomla plugins work. @@ -20,6 +26,8 @@ The process of involving the plugins comprises 2 steps: 1. Import all plugins of a given type 2. Trigger an event +## Importing a Plugin + Importing a plugin type is implemented by the code: ```php PluginHelper::importPlugin($pluginType, …); @@ -36,12 +44,16 @@ Importing a plugin type involves: Splitting the plugins by plugin type in this way makes Joomla more performant - it doesn't need to process plugins which aren't going to be interested in the event which will be triggered next. +## Triggering an Event + The second step is triggering an event via a call to the event dispatcher. Joomla looks through its store of `Listeners` to determine which plugins have subscribed to that event type. It then calls the associated method of the subscribing plugin, and passes the event data to it, often allowing that data to be modified by the plugin code. Each plugin which has subscribed to that event is called in turn (based on a priority scheme), and any results returned by the plugins are collated into an array associated with the event. As an example, after Joomla is initialised system plugins are imported and the event `onAfterInitialise` is triggered. The "Remember Me" plugin (in plugins/system/remember) receives notification of this, and can log in a user who has previously checked the "Remember Me" checkbox on the login form. +## Subsequent Events + Once a plugin has been imported and its subscriptions logged in the `Listeners` data store, then it continues to receive notifications for all the events it has subscribed to. For example, consider the following scenario: - system plugins are imported, and the `onAfterInitialise` event is triggered - some time later content plugins are imported, and the `onContentPrepare` event is triggered. @@ -50,6 +62,8 @@ The system plugins imported in the first step can subscribe to the `onContentPre You should also be aware that unlike components and modules, there aren't different site plugins and administrator plugins. The plugins which you install are run for all the different contexts - site, administrator and API applications - so it's a good idea to in your plugins to check the application context. +## Sequence Diagram + For those who wish a more detailed picture of how plugins work, see the sequence diagram below. ```mermaid diff --git a/docs/building-extensions/plugins/joomla-4-and-5-changes.md b/docs/building-extensions/plugins/joomla-4-and-5-changes.md deleted file mode 100644 index 76af7ef9..00000000 --- a/docs/building-extensions/plugins/joomla-4-and-5-changes.md +++ /dev/null @@ -1,284 +0,0 @@ ---- -title: Joomla 4 and 5 Changes -sidebar_position: 2 ---- - -Joomla 4 and 5 Changes -====================== - -## Key changes -In Joomla 4 a number of significant changes began to be introduced to the plugins area: -1. Plugins were instantiated via the Dependency Injection Container, through the `services/provider.php` file. Previously plugins were run via the plugin's main php file. -2. Plugins subscribed to the events which they wanted to get notified about. Previously you wrote a method whose name matched the name of the event, and Joomla used PHP reflection classes to determine when your method got called. With the subscription mechanism Joomla checks if your plugin implements `Joomla\Event\SubscriberInterface` and if so calls `getSubscribedEvents` to which the plugin returns the event types it wants to handle, and the method which Joomla should call: -```php -public static function getSubscribedEvents(): array -{ - return [ - 'onContentPrepare' => 'myContentPrepareMethod', - 'onContentAfterTitle' => 'myContentAfterTitleMethod', - ]; -} -``` -3. Events were passed to plugins as Joomla Event objects. Previously each method associated with an event had parameters passed which were specific to that event type. For example, the method called when `onContentPrepare` was triggered was: -```php -public function onContentPrepare($context, &$row, $params, $page = 0) -``` -In Joomla 3 "events" basically comprised -- a string with the event name eg "onContentPrepare", and -- associated parameters, which were passed to the `onContentPrepare` method. - -The aim of the Joomla team has been to replace these with event classes, with each type of event having its own concrete event class. For example, for the `onContentPrepare` event there would be a `ContentPrepareEvent` class, with properties `$context`, `$row`, `$params` and `$page`. However, the process of replacing all the current code has been spread over a number of Joomla releases, some being done in Joomla 4 and some in Joomla 5. - -To enable plugins to use Event classes before the full concrete event class could be implemented a temporary solution was adopted, that is, to provide a `GenericEvent` class, which meant that plugins could be developed which used the Event parameter in their methods, rather than the list of parameters. - -**It's important to understand that the way you obtain the parameters of your plugin method, and the way which you provide your return value is different depending on whether a GenericEvent or a concrete class is used.** - -This is what we're now going to look at in detail. - -## Joomla 3 / 4 / 5 comparison -Let's take the example of `onContentPrepare` and consider how it has changed through these releases. - -### Joomla 3 -In Joomla 3 the event was triggered in `com_content` using -```php -$dispatcher->trigger('onContentPrepare', array ('com_content.article', &$item, &$item->params, $offset)); -``` -This resulted in a call to your plugin method: -```php -public function onContentPrepare($context, &$row, $params, $page = 0) -``` -and Joomla found this method by using the reflection class of your plugin class. - -`onContentPrepare` doesn't take any return value, but other plugins such as `onContentAfterTitle` do. To specify the return value you simply did -```php -return $value; -``` - -The Joomla team have provided backward compatibility for plugins through releases 4 and 5, so this mechanism will still work. However it will likely disappear in Joomla 6, so you should not write any new plugins this way. - -### Joomla 4 -In Joomla 4 the event is triggered using: -```php -$this->dispatchEvent(new Event('onContentPrepare', ['com_content.article', &$item, &$item->params, $offset])); -``` -This creates a `GenericEvent` which has Argument fields for the parameters above, which can be obtained via the `getArguments` method (using PHP array destructuring): -```php -[$context, $article, $params, $page] = array_values($event->getArguments()); -``` -The `array_values` is not really necessary here, but we'll see the reason for it shortly. - -For `GenericEvent` the `result` is an array which is held within the `GenericEvent` class, and you have to add any return value from your plugin into this array. So to return a value from a plugin method using `GenericEvent` you use: -```php -$result = $event->getArgument('result') ?: []; // get the result argument from GenericEvent -$result[] = $value; // add your return value into the array -$event->setArgument('result', $result); // write back the updated result into the GenericEvent instance -``` - -### Joomla 5 -In Joomla 5 the event is triggered using: -```php -$dispatcher->dispatch('onContentPrepare', new Content\ContentPrepareEvent('onContentPrepare', $contentEventArguments)); -``` -Here the concrete event class `ContentPrepareEvent` (from libraries/src/Event/Content/ContentPrepareEvent.php) is used. Such events may have specific getter methods (eg `getContext`), but you can still get the parameters using: -```php -[$context, $article, $params, $page] = array_values($event->getArguments()); -``` -Here the `array_values` is necessary, so if you use this to obtain the parameters it will work for both `GenericEvent` and concrete Event classes. - -To return a value your plugin should use `$event->addResult($value)`, but you can check if this method exists by -```php -use Joomla\CMS\Event\Result\ResultAwareInterface; -... - if ($event instanceof ResultAwareInterface) { - $event->addResult($value); - return; - } else { - // use GenericEvent approach - } -``` - -**If you're developing a developing a plugin to handle a `GenericEvent` then it's crucial to use the above mechanisms to avoid the plugin failing whenever the triggering code moves to using a concrete Event class.** - -## Summary - Accessing Event Arguments - -### Concrete Event Class - -You can use this approach if under libraries/src/Event there is a Joomla event class (known as a "concrete" event class) which is specific to the plugin event group. (Only a limited set is available for Joomla 4). - -```php -use Joomla\CMS\Plugin\CMSPlugin; -use Joomla\Event\SubscriberInterface; -use Joomla\CMS\Event\Content\ContentPrepareEvent; - -class MyPlugin extends CMSPlugin implements SubscriberInterface -{ - public static function getSubscribedEvents(): array - { - return [ - 'onContentPrepare' => 'myOnContentPrepare', - ]; - } - - public function myOnContentPrepare(ContentPrepareEvent $event) - { - $context = $event->getContext(); - $item = $event->getItem(); - $params = $event->getParams(); - $page = $event->getPage(); - // ... - } -``` - -Here in the getter call you must use the correct name for the argument, and in the Joomla manual documentation the correct name is always specified. - -You can also find the getter methods in the API documentation for the event class, for example [Event/Content/ContentPrepareEvent methods](cms-api://classes/Joomla-CMS-Event-Content-ContentPrepareEvent.html). - -To use an event class your plugin class must implement \Joomla\Event\SubscriberInterface and provide the `getSubscribedEvents` function. Your plugin listener function must then have just the single `$event` parameter. - -### Generic Event Class - -Use this approach after Joomla 4.0 if you wish to use event classes, but a concrete event class isn't available for your target Joomla version. - -```php -use Joomla\CMS\Plugin\CMSPlugin; -use Joomla\Event\SubscriberInterface; -use Joomla\Event\Event; - -class MyPlugin extends CMSPlugin implements SubscriberInterface -{ - public static function getSubscribedEvents(): array - { - return [ - 'onContentPrepare' => 'myOnContentPrepare', - ]; - } - - public function myOnContentPrepare(Event $event) - { - [$context, $item, $params, $page] = array_values($event->getArguments()); - ... - } -``` - -This approach also works for concrete event classes. - -It depends on the order of the arguments in the event class, and the correct order is always given in the documentation. - -There is a commitment from the Joomla team to preserve the same order within the event arguments as within the order of the parameters of the legacy method, but this will be removed in Joomla 6. This is implemented via the variable `$legacyArgumentsOrder`, eg in Joomla\CMS\Event\Content\ContentPrepareEvent (in libraries/src/Event/Content/ContentPrepareEvent.php): - -```php -class ContentPrepareEvent extends ContentEvent -{ - protected $legacyArgumentsOrder = ['context', 'subject', 'params', 'page']; -``` - -### ArrayAccess - -The Joomla Event class supports the [PHP ArrayAccess interface](https://www.php.net/manual/en/class.arrayaccess.php). -So you can use this method to obtain the arguments for both generic events and concrete events: - -```php -use Joomla\Event\Event; - - public function myOnContentPrepare(Event $event) - { - $context = $event['context']; - } -``` - -If the 'context' array element / argument doesn't exist then `$event['context']` returns null, -so you can use this to check if an argument exists. - -### Traditional Legacy Method - -You will be using this method if you developed your plugin before Joomla 4.0. In this case, you're probably better to continue this approach until concrete event classes are available for all the events your plugin listens for. - -You shouldn't use this approach for developing a new plugin, as you will have to rework it. - -To use the traditional (legacy) method, you specify a public function which has the same name as the event name, and Joomla uses PHP reflection to find your method. You specify the event parameters in your function signature, for example: - -```php -use Joomla\CMS\Plugin\CMSPlugin; - -class MyPlugin extends CMSPlugin -{ - public function onContentPrepare($context, $item, $params, $page) - { - if ($context == "com_content.article") ... - } -``` - -It doesn't matter what you specify as the names of your function parameters, just on their order in the parameter sequence. -The documentation in the Joomla manual always specifies the correct order of these arguments. - -## Summary - Returning Values - -### Concrete Event Class - -To return a value when using a concrete event class use `$event->addResult()`: - -```php -use Joomla\CMS\Plugin\CMSPlugin; -use Joomla\CMS\Event\Content\AfterTitleEvent; - -class MyPlugin extends CMSPlugin - - public function onContentAfterTitle(AfterTitleEvent $event) - { - ... - $event->addResult($value); - } -``` - -### Generic Event Class - -To return a value when using a generic event class use the following: - -```php -use Joomla\CMS\Plugin\CMSPlugin; -use Joomla\Event\Event; - -class MyPlugin extends CMSPlugin - - public function onContentAfterTitle(Event $event) - { - ... - $result = $event->getArgument('result') ?: []; // get the result argument from GenericEvent - $result[] = $value; // add your return value into the array - $event->setArgument('result', $result); - } -``` - -This approach will also work with concrete event classes. - -### Traditional Legacy Method - -To return a value when using the legacy method use the PHP `return`: - -``` - public function onContentAfterTitle($context, $item, $params, $page) - { - ... - return $value; - } -``` - -## Other New Features -### Priority -To use a plugin priority other than the default, specify this in your response to `getSubscribedEvents` -```php -public static function getSubscribedEvents(): array -{ - return [ - 'onContentPrepare' => ['myContentPrepareMethod', \Joomla\Event\Priority::HIGH], - 'onContentAfterTitle' => ['myContentAfterTitleMethod', \Joomla\Event\Priority::MIN], - ]; -} -``` -See the other values available in libraries/vendor/joomla/event/src/Priority.php. Use with caution though, as this will override any priority which the site administrator may want to set through ordering the plugins. - -### Stop Propagation -To stop the propagation of an event to other plugins you can call -```php -$event->stopPropagation(); -``` diff --git a/docs/building-extensions/plugins/methods-and-arguments.md b/docs/building-extensions/plugins/methods-and-arguments.md new file mode 100644 index 00000000..c8829589 --- /dev/null +++ b/docs/building-extensions/plugins/methods-and-arguments.md @@ -0,0 +1,298 @@ +--- +title: Plugin Methods and Arguments +sidebar_position: 2 +--- + +This section describes some general features of plugin code, +and many of these are used in the [plugin tutorial](./basic-content-plugin.md) which follows. + +Plugin Methods and Arguments +============================ + +There are 2 functions which you must implement within your plugin: + +1. The `getSubscribedEvents()` method. You tell Joomla + - which events you want to subscribe to + - the code to run when one of those events is triggered + +2. The handler function which Joomla should call when one of those events is triggered. + +This section describes these functions in detail. + +A note on terminology: you will find that the terms "dispatch", "trigger" and "emit" an event +are used interchangeably in both the documentation and in the Joomla code. + +## getSubscribedEvents method + +In this function you simply return an associative array where + +- the index is the event name +- the value is the name of the function in your plugin which handles that event + +```php +use Joomla\CMS\Plugin\CMSPlugin; +use Joomla\Event\SubscriberInterface; + +class MyPlugin extends CMSPlugin implements SubscriberInterface +{ + public static function getSubscribedEvents(): array + { + return [ + 'onContentPrepare' => 'modifyContent', + 'onContentAfterTitle' => 'addSubtitle', + ]; + } +``` + +It's important that you state that your plugin `implements SubscriberInterface`. + +## Your handler function + +Here is an example based on the [onContentPrepare event](./plugin-events/content.md#oncontentprepare): + +```php +use \Joomla\CMS\Event\Content\ContentPrepareEvent; + +public function modifyContent(ContentPrepareEvent $event): void +{ + if (!$this->getApplication()->isClient('site')) { + return; + } + $args = $event->getArguments(); + $item = $event->getItem(); + if ($context = $event['context']) { + ... + } +} +``` + +The subsections below provide some detailed descriptions. + +### Event Class + +Each event has its own PHP class, and you can find these in libraries/src/Event. +Some event classes are in this directory, but most are in subdirectories categorised by the area of Joomla where that event is triggered. + +The name of the event is often similar to the class name, +eg the event class \Joomla\CMS\Event\Content\ContentPrepareEvent is associated with the event onContentPrepare. +This is an event which is of type "content", in the sense that plugins of type "content" are imported prior to it being triggered. +However, there isn't a "type" field associated with the event instance which defines this plugin type (`$event` doesn't have a "type" property). + +There is a mapping between many event names and event classes in libraries/src/Event/CoreEventAware.php. + +You may be wondering: "How do I find out which event to use to achieve what I want?" + +If a suitable event isn't documented in this manual, and a response from AI doesn't give you a useful answer, +then there are a few options to consider: + +- look through the event classes below libraries/src/Event or in libraries/vendor/composer/autoload_classmap.php +- check for the mapping to event name in libraries/src/Event/CoreEventAware.php +- check the outdated [Plugin Events documentation](https://docs.joomla.org/Plugin/Events) +- search the Joomla code for the possible event names and event class names. + +### Checking the Client + +It's important to remember that plugins are run whenever Joomla is run: + +- the front-end site +- the back-end administrator +- using the Joomla API +- running a console job + +So in your event handler code you should generally check the context using something like: + +```php +if (!$this->getApplication()->isClient('site')) { + return; +} +``` + +The [getApplication](#getapplication) function is an inherited method, as described below. + +### Arguments + +Each Event instance has a number of arguments which are documented in the detailed plugin event pages +(and sometimes in the source code of the event class). + +You can obtain these by: + +```php +$args = $event->getArguments(); +``` + +The returned value is an associative array mapping the arguments to their values, eg: + +```php +["context" => "com_content.article", "subject" => , + "params" =>
    , "page" => 0] +``` + +### Getter Functions + +The preferred way is to get the arguments using the specific getter functions, for example + +```php +$context = $event->getContext(); +$item = $event->getItem(); +$params = $event->getParams(); +$page = $event->getPage(); +``` + +The getter function usual matches the name of the argument **but this is not always the case**. + +For example, for ContentPrepareEvent the 'subject' argument is obtained using `getItem()`. + +Some arguments don't have a getter function at all, and you have to use the argument array index approach. + +These situations will be highlighted in the detailed plugin events pages. + +### Array Access + +The Joomla Event class supports the [PHP ArrayAccess interface](https://www.php.net/manual/en/class.arrayaccess.php). +So you can use this method to obtain the arguments, and you may find this useful in the case where an explicit getter function is not available. + +```php +use \Joomla\CMS\Event\Content\ContentPrepareEvent; + + public function modifyContent(ContentPrepareEvent $event): void + { + if ($context = $event['context']) { + ... + } + } +``` + +If the 'context' array element / argument doesn't exist then `$event['context']` returns null, +so you can use this to check if an argument exists. No PHP warning is generated if the array index is not present. + +### Updating arguments and returning results + +The documentation will state whether any arguments are modifiable, or whether a result should be returned. + +For example, ContentPrepareEvent passes a 'subject' argument (available via `getItem()`) which you can modify. +Depending upon the 'context' argument this item may be an article, a contact, a user, etc., +and you can set properties of this object. + +```php +$context = $event->getContext(); +if ($context === "com_content.article") { + $article = $event->getItem(); + $article->text = $article->text . " [end of text]"; // Appends " [end of text]" to the end of the article +} +``` + +(In this case the appended text would be included in the HTTP response, but not saved to the database). + +If the event allows a result to be returned then you should use this approach: + +```php +use Joomla\CMS\Event\Content\AfterTitleEvent; + + public function onContentAfterTitle(AfterTitleEvent $event) + { + ... + $event->addResult($value); + } +``` + +Sometimes the event provides a specific function which allows you to update an entity. +For example, the [onInstallerBeforeInstaller](./plugin-events/installer.md#oninstallerbeforeinstaller) event +allows the plugin to change the package which is being installed: + +```php +public function onInstallerBeforeInstaller(\Joomla\CMS\Event\Installer\BeforeInstallerEvent $event): void +{ + $newpkg = array("dir" => $pathToExtensionDirectory, "type" => "plugin", "extractdir" => null, "packagefile" => null); + $event->updatePackage($newpkg); +} +``` + +## Methods available to Plugins + +Inside your plugin you have access to several methods. + +### getApplication + +- `$this->getApplication()` returns the Application instance. (This method is inherited from CMSPlugin). +However, you need to inject this dependency in your plugin's services/provider.php file. + +```php + $plugin = new MyPlugin($subject, $config); + $app = Joomla\CMS\Factory::getApplication(); + $plugin->setApplication($app); +``` + +From the `$app` instance you can find other details +such as the global configuration (getConfig), current user (getIdentity), language (getLanguage), document (getDocument), etc. + +### loadLanguage + +- `$this->loadLanguage()` (also inherited) will load your plugin's language constants. +You have to load them before using them in your code. + +Alternatively you can set within your plugin class: + +```php +protected $autoloadLanguage = true; +``` + +and Joomla will load your plugin's language constants for you. + +### Injected Dependencies + +If your plugin needs access to other Joomla class instances which are in the main Dependency Injection container then you should follow this pattern: + +- in your services/provider.php file get the instance out of the DI container and pass it into your plugin's setter method + +- in your plugin use your getter method to retrieve it. + +- include these getter and setter methods in your plugin by `use` + the appropriate trait + +For example, to include the database object do this in your services/provider.php file: + +```php + $plugin = new MyPlugin( (array) PluginHelper::getPlugin('content', 'myplugin') ); + $plugin->setDatabase($container->get(DatabaseInterface::class)); +``` + +and include this in your plugin file: + +```php +use Joomla\CMS\Plugin\CMSPlugin; +use Joomla\Event\SubscriberInterface; +use Joomla\Database\DatabaseAwareTrait; + +class MyPlugin extends CMSPlugin implements SubscriberInterface +{ + use DatabaseAwareTrait; + + public function modifyContent(ContentPrepareEvent $event): void + { + $db = $this->getDatabase(); + } +} +``` + +As another example, if your plugin sends mail then you can set the MailerFactory as a dependency. + +```php title="services/provider.php" + $plugin = new MyPlugin( (array) PluginHelper::getPlugin('content', 'myplugin') ); + $plugin->setMailerFactory($container->get(MailerFactoryInterface::class)); +``` + +```php title="plugin file" +use Joomla\CMS\Mail\MailerFactoryAwareTrait; + +class MyPlugin extends CMSPlugin implements SubscriberInterface +{ + use MailerFactoryAwareTrait; + + public function modifyContent(ContentPrepareEvent $event): void + { + $mailer = $this->getMailerFactory()->createMailer(); + } +} +``` + +These traits are scattered throughout the Joomla libraries directories, +but you can find them by searching for filenames which match `*AwareTrait.php`. \ No newline at end of file diff --git a/docs/building-extensions/plugins/plugin-events/content.md b/docs/building-extensions/plugins/plugin-events/content.md index 0d7ae546..11dcae9f 100644 --- a/docs/building-extensions/plugins/plugin-events/content.md +++ b/docs/building-extensions/plugins/plugin-events/content.md @@ -15,7 +15,7 @@ They are triggered during the process of: The sections below give a brief description of each content event, what the event parameters / arguments are, and any examples of their use which are available in the Joomla Manual. -For background on Joomla transitioning to using classes for events see [Joomla 4 and 5 changes](../joomla-4-and-5-changes.md), where you can also find explanations for [accessing the arguments](../joomla-4-and-5-changes.md#summary---accessing-event-arguments) and [returning values](../joomla-4-and-5-changes.md#summary---returning-values). +For background on obtaining arguments and returning results see [Plugin Methods and Arguments](../methods-and-arguments.md). ## onContentPrepare diff --git a/docs/building-extensions/plugins/plugin-events/index.md b/docs/building-extensions/plugins/plugin-events/index.md index fdd2fb05..8b28a5b7 100644 --- a/docs/building-extensions/plugins/plugin-events/index.md +++ b/docs/building-extensions/plugins/plugin-events/index.md @@ -8,8 +8,6 @@ List of Plugin Events The list of events below includes the event name and a short description of its use, together with a link to the detailed description. -As described in [Joomla 4 and 5 changes](../joomla-4-and-5-changes.md), Joomla events have changed from being strings with associated parameters to "concrete" event classes specific to each event, sometimes via a "generic" event class. Some concrete event classes were introduced in Joomla 4 and others in Joomla 5. If you want your plugin to support both concrete and generic event classes then you need to code it as described in [Joomla 4 and 5 changes](../joomla-4-and-5-changes.md). - The event Group refers to the group of plugins which Joomla ensures are imported prior to dispatching that event. | Event Name | Short Description | Group | From Release | diff --git a/docs/building-extensions/plugins/plugin-events/installer.md b/docs/building-extensions/plugins/plugin-events/installer.md index 4a1728c5..c36dcffa 100644 --- a/docs/building-extensions/plugins/plugin-events/installer.md +++ b/docs/building-extensions/plugins/plugin-events/installer.md @@ -13,9 +13,7 @@ Installer plugin events are triggered when some routines are performed during th For an overview of how a number of these events fit into the installation process see [Install Process](../../install-update/installation/install-process.md). (Note that the onExtensionBeforeInstall and onExtensionAfterInstall events aren't covered here). -For background on Joomla transitioning to using classes for events see [Joomla 4 and 5 changes](../joomla-4-and-5-changes.md), -where you can also find explanations for [accessing the arguments](../joomla-4-and-5-changes.md#summary---accessing-event-arguments) -and [returning values](../joomla-4-and-5-changes.md#summary---returning-values). +For background on obtaining arguments and returning results see [Plugin Methods and Arguments](../methods-and-arguments.md). ## onInstallerAddInstallationTab diff --git a/docs/building-extensions/plugins/plugin-examples/ajax-plugin.md b/docs/building-extensions/plugins/plugin-examples/ajax-plugin.md index 3ba3e77e..b54567ef 100644 --- a/docs/building-extensions/plugins/plugin-examples/ajax-plugin.md +++ b/docs/building-extensions/plugins/plugin-examples/ajax-plugin.md @@ -136,7 +136,7 @@ use My\Plugin\Ajax\AjaxJobs\Extension\Jobs; ``` ### Jobs file -This is where you write your code for your ad hoc jobs. The result is returned as described in [Joomla 4 and 5 Changes](../joomla-4-and-5-changes.md). +This is where you write your code for your ad hoc jobs. ```php title="plg_ajax_jobs/src/Extension/Jobs.php" "; } - if ($event instanceof ResultAwareInterface) { - $event->addResult($output); - } else { - $result = $event->getArgument('result') ?? []; - $result[] = $output; - $event->setArgument('result', $result); - } + $event->addResult($output); } } ``` From 3e7dc1fec11c09cc46a99260d8c3cf3ef262c362 Mon Sep 17 00:00:00 2001 From: Robbie Jackson Date: Tue, 9 Dec 2025 11:44:25 +0000 Subject: [PATCH 2/5] review comments addressed --- .../plugins/advanced-features.md | 12 +++- .../plugins/methods-and-arguments.md | 57 +++++++++++-------- .../plugins/plugin-events/application.md | 42 ++++++-------- .../plugins/plugin-events/installer.md | 44 +++----------- 4 files changed, 69 insertions(+), 86 deletions(-) diff --git a/docs/building-extensions/plugins/advanced-features.md b/docs/building-extensions/plugins/advanced-features.md index c25a5259..b839e696 100644 --- a/docs/building-extensions/plugins/advanced-features.md +++ b/docs/building-extensions/plugins/advanced-features.md @@ -6,9 +6,15 @@ sidebar_position: 4 Plugin Advanced Features ======================== -## Priority +## Priority of Events -To use a plugin priority other than the default, specify this in your response to `getSubscribedEvents` +Often there will be several plugins listening for the same event. + +The order in which these plugins' handler functions are called is by default controlled by the order the plugins are read in from the database, +and this is determined by the order of the plugins set by the administrator in the Manage / Plugins page on the back-end. + +You can change the priority of your plugin's handler function on a per-event basis. +To do this, specify the priority in your response to `getSubscribedEvents` an array of `[, ]` ```php public static function getSubscribedEvents(): array @@ -23,7 +29,7 @@ public static function getSubscribedEvents(): array See the other values available in libraries/vendor/joomla/event/src/Priority.php. Use with caution though, as this will override any priority which the site administrator may want to set through ordering the plugins. -## Stop Propagation +## Stop Event Propagation To stop the propagation of an event to other plugins you can call diff --git a/docs/building-extensions/plugins/methods-and-arguments.md b/docs/building-extensions/plugins/methods-and-arguments.md index c8829589..dcc8a259 100644 --- a/docs/building-extensions/plugins/methods-and-arguments.md +++ b/docs/building-extensions/plugins/methods-and-arguments.md @@ -46,6 +46,8 @@ class MyPlugin extends CMSPlugin implements SubscriberInterface It's important that you state that your plugin `implements SubscriberInterface`. +(Note that you can also set a priority for each event and handler - see [Priority of Events](./advanced-features.md#priority-of-events)). + ## Your handler function Here is an example based on the [onContentPrepare event](./plugin-events/content.md#oncontentprepare): @@ -58,9 +60,10 @@ public function modifyContent(ContentPrepareEvent $event): void if (!$this->getApplication()->isClient('site')) { return; } - $args = $event->getArguments(); - $item = $event->getItem(); - if ($context = $event['context']) { + $item = $event->getItem(); // preferred way to get the event arguments + $args = $event->getArguments(); // fallback mechanism + $context = $event['context']; // using PHP Array Access + if ($context) { ... } } @@ -109,42 +112,42 @@ if (!$this->getApplication()->isClient('site')) { The [getApplication](#getapplication) function is an inherited method, as described below. -### Arguments +### Getter Functions Each Event instance has a number of arguments which are documented in the detailed plugin event pages (and sometimes in the source code of the event class). - -You can obtain these by: + +You should use the event getter functions to obtain the event arguments, for example ```php -$args = $event->getArguments(); +$context = $event->getContext(); +$item = $event->getItem(); +$params = $event->getParams(); +$page = $event->getPage(); ``` -The returned value is an associative array mapping the arguments to their values, eg: +The getter function matches the name of the argument in the documentation. -```php -["context" => "com_content.article", "subject" => , - "params" =>
    , "page" => 0] -``` +`$event->getName()` returns the name of the event. -### Getter Functions +### Arguments -The preferred way is to get the arguments using the specific getter functions, for example +Occasionally you may find that there isn't a getter function for a particular argument. +In this case you can obtain the event arguments by: ```php -$context = $event->getContext(); -$item = $event->getItem(); -$params = $event->getParams(); -$page = $event->getPage(); +$args = $event->getArguments(); ``` -The getter function usual matches the name of the argument **but this is not always the case**. - -For example, for ContentPrepareEvent the 'subject' argument is obtained using `getItem()`. +The returned value is an associative array mapping the arguments to their values, eg: -Some arguments don't have a getter function at all, and you have to use the argument array index approach. +```php +["context" => "com_content.article", "subject" => , + "params" =>
    , "page" => 0] +``` -These situations will be highlighted in the detailed plugin events pages. +(Note that the array element index doesn't always match the argument in the documentation pages. +For example, for ContentPrepareEvent the 'item' argument is given by the "subject" element in the array.) ### Array Access @@ -156,7 +159,8 @@ use \Joomla\CMS\Event\Content\ContentPrepareEvent; public function modifyContent(ContentPrepareEvent $event): void { - if ($context = $event['context']) { + $context = $event['context']; + if ($context) { ... } } @@ -238,6 +242,11 @@ protected $autoloadLanguage = true; and Joomla will load your plugin's language constants for you. +:::warning + This feature has a negative effect on performance, as it means that your language constants are processed even when they're not used. + You should always load them manually. +::: + ### Injected Dependencies If your plugin needs access to other Joomla class instances which are in the main Dependency Injection container then you should follow this pattern: diff --git a/docs/building-extensions/plugins/plugin-events/application.md b/docs/building-extensions/plugins/plugin-events/application.md index 9e96aae2..2c9c5249 100644 --- a/docs/building-extensions/plugins/plugin-events/application.md +++ b/docs/building-extensions/plugins/plugin-events/application.md @@ -54,7 +54,7 @@ If you would like further information then you may find it helpful to watch the ## Application Events overview The application event classes can be found in libraries/src/Event/Application. -In general they have a single parameter `subject` which is set to the Application instance, +In general they have a single parameter `application` which is set to the Application instance, and the getter method for this parameter is `getApplication`, for example: ```php @@ -133,7 +133,7 @@ public function onAfterInitialise(AfterInitialiseEvent $event): void The event class \Joomla\CMS\Event\Application\AfterInitialiseEvent has the following arguments: -- **`subject`** - the Application instance, available via `$event->getApplication()` +- **`application`** - the Application instance, available via `$event->getApplication()` ### Return Value @@ -151,7 +151,7 @@ You can use this event, for example, to tidy up aspects after the routing functi The event class \Joomla\CMS\Event\Application\AfterRouteEvent has the following arguments: -- **`subject`** - the Application instance, available via `$event->getApplication()` +- **`application`** - the Application instance, available via `$event->getApplication()` ### Return Value @@ -169,9 +169,9 @@ You can use this event, to set aspects of the Document. The event class \Joomla\CMS\Event\Application\AfterInitialiseDocumentEvent has the following arguments: -- **`subject`** - the Application instance, available via `$event->getApplication()` +- **`application`** - the Application instance, available via `$event->getApplication()` -- **`document`** - The Document. You can use the getter method `$doc = $event->getDocument();` to get this. +- **`document`** - The Document, available via `$event->getDocument()` ### Return Value @@ -187,7 +187,7 @@ This event is triggered after Joomla has run the main component and buffered its The event class \Joomla\CMS\Event\Application\AfterDispatchEvent has the following arguments: -- **`subject`** - the Application instance, available via `$event->getApplication()` +- **`application`** - the Application instance, available via `$event->getApplication()` ### Return Value @@ -204,7 +204,7 @@ to fill in the `` tags. The event class \Joomla\CMS\Event\Application\BeforeRenderEvent has the following arguments: -- **`subject`** - the Application instance, available via `$event->getApplication()` +- **`application`** - the Application instance, available via `$event->getApplication()` ### Return Value @@ -222,9 +222,9 @@ You can use this to include other items within the ``, via the [Web Asset The event class \Joomla\CMS\Event\Application\BeforeCompileHeadEvent has the following arguments: -- **`subject`** - the Application instance, available via `$event->getApplication()` +- **`application`** - the Application instance, available via `$event->getApplication()` -- **`document`** - The Document. You can use the getter method `$doc = $event->getDocument();` to get this. +- **`document`** - The Document, available via `$event->getDocument()` ### Return Value @@ -252,7 +252,7 @@ public function onAfterRender(AfterRenderEvent $event): void The event class \Joomla\CMS\Event\Application\AfterRenderEvent has the following arguments: -- **`subject`** - the Application instance, available via `$event->getApplication()` +- **`application`** - the Application instance, available via `$event->getApplication()` ### Return Value @@ -268,7 +268,7 @@ This event is triggered just before Joomla sends the HTTP response. The event class \Joomla\CMS\Event\Application\BeforeRespondEvent has the following arguments: -- **`subject`** - the Application instance, available via `$event->getApplication()` +- **`application`** - the Application instance, available via `$event->getApplication()` ### Return Value @@ -284,7 +284,7 @@ This event is triggered just after Joomla sends the HTTP response. The event class \Joomla\CMS\Event\Application\AfterRespondEvent has the following arguments: -- **`subject`** - the Application instance, available via `$event->getApplication()` +- **`application`** - the Application instance, available via `$event->getApplication()` ### Return Value @@ -304,7 +304,7 @@ which could be used by the extension's `boot()` method (which is run when the ex The event class \Joomla\CMS\Event\BeforeExtensionBootEvent has the following arguments: -- **`subject`** - the Application instance, but note that there is no getter function for this. Instead you can use: +- **`application`** - the Application instance, but note that there is no getter function for this. Instead you can use: ```php use \Joomla\CMS\Event\BeforeExtensionBootEvent; @@ -315,14 +315,9 @@ public function onBeforeExtensionBoot(BeforeExtensionBootEvent $event): void } ``` -- **`type`** - This is the type of extension being booted, +- **`extensionType`** - This is the type of extension being booted, identified by the interface it supports, eg "Joomla\CMS\Extension\PluginInterface". - -Note that the getter function for this argument is getExtensionType rather than getType: - -```php -$type = $event->getExtensionType(); -``` +Available via `$event->getExtensionType()` - **`extensionName`** - The extension name, available via `$event->getExtensionName()`: @@ -361,7 +356,7 @@ then com_content will run your MVC classes instead of its own. The event class \Joomla\CMS\Event\AfterExtensionBootEvent has the following arguments: -- **`subject`** - the Application instance, but note that there is no getter function for this. Instead you can use: +- **`application`** - the Application instance, but note that there is no getter function for this. Instead you can use: ```php use \Joomla\CMS\Event\AfterExtensionBootEvent; @@ -372,10 +367,9 @@ public function onBeforeExtensionBoot(AfterExtensionBootEvent $event): void } ``` -- **`type`** - This is the type of extension booted, +- **`extensionType`** - This is the type of extension being booted, identified by the interface it supports, eg "Joomla\CMS\Extension\PluginInterface". - -Note that the getter function for this argument is getExtensionType rather than getType: +Available via `$event->getExtensionType()` ```php $type = $event->getExtensionType(); diff --git a/docs/building-extensions/plugins/plugin-events/installer.md b/docs/building-extensions/plugins/plugin-events/installer.md index c36dcffa..701c0656 100644 --- a/docs/building-extensions/plugins/plugin-events/installer.md +++ b/docs/building-extensions/plugins/plugin-events/installer.md @@ -27,7 +27,11 @@ The event class `\Joomla\CMS\Event\Installer\AddInstallationTabEvent` has no arg ### Return Value -Return the contents of the additional tab which you want to include in the options for install. +Return the contents of the additional tab which you want to include in the options for install using, eg + +```php +$event->addResult($tabHTML); +``` ### Examples @@ -101,17 +105,7 @@ This event is triggered at the beginning of an installation operation. The event class `\Joomla\CMS\Event\Installer\BeforeInstallationEvent` has the following arguments: -- **`subject`** - This will be the FQN of the install model class Joomla\Component\Installer\Administrator\Model\InstallModel - -Note that there is no getter for the subject argument. Instead you should use: - -```php -public function onInstallerBeforeInstallation(\Joomla\CMS\Event\Installer\BeforeInstallationEvent $event): void -{ - $args = $event->getArguments(); - $subject = $args['subject']; -} -``` +- **`model`** - The instance of the install model class (\Joomla\Component\Installer\Administrator\Model\InstallModel) - **`package`** - This is null. @@ -136,17 +130,7 @@ but before Joomla has started to process the extension's installation files. The event class `\Joomla\CMS\Event\Installer\BeforeInstallerEvent` has the following arguments: -- **`subject`** - This will be the FQN of the install model class Joomla\Component\Installer\Administrator\Model\InstallModel - -Note that there is no getter for the subject argument. Instead you should use: - -```php -public function onInstallerBeforeInstaller(\Joomla\CMS\Event\Installer\BeforeInstallerEvent $event): void -{ - $args = $event->getArguments(); - $subject = $args['subject']; -} -``` +- **`model`** - The instance of the install model class (\Joomla\Component\Installer\Administrator\Model\InstallModel) - **`package`** - This is an array of 4 elements, which in the case of uploading a zip file will contain: @@ -186,21 +170,11 @@ This event is triggered after extension has been installed. The event class `\Joomla\CMS\Event\Installer\AfterInstallerEvent` has the following arguments: -- **`subject`** - This will be the FQN of the install model class Joomla\Component\Installer\Administrator\Model\InstallModel - -Note that there is no getter for the subject argument. Instead you should use: - -```php -public function onInstallerAfterInstaller(\Joomla\CMS\Event\Installer\AfterInstallerEvent $event): void -{ - $args = $event->getArguments(); - $subject = $args['subject']; -} -``` +- **`model`** - The instance of the install model class (\Joomla\Component\Installer\Administrator\Model\InstallModel) - **`package`** - The package which has been installed, in the form described in onInstallerBeforeInstaller above. -- **`installer`** - The installer class - Joomla sets this to "Joomla\CMS\Installer\Installer" +- **`installer`** - The instance of installer class (\Joomla\CMS\Installer\Installer) - **`installerResult`** - true or false, depending upon whether the package was successfully installed or not From a8583d63e1832666bd63fe70860f3f18062fdc64 Mon Sep 17 00:00:00 2001 From: Robbie Jackson Date: Wed, 10 Dec 2025 18:35:54 +0000 Subject: [PATCH 3/5] remove tutorial zip download and link to manual instead --- .../plugins/_assets/plg_shortcodes.zip | Bin 4097 -> 0 bytes .../plugins/basic-content-plugin.md | 4 +++- 2 files changed, 3 insertions(+), 1 deletion(-) delete mode 100644 docs/building-extensions/plugins/_assets/plg_shortcodes.zip diff --git a/docs/building-extensions/plugins/_assets/plg_shortcodes.zip b/docs/building-extensions/plugins/_assets/plg_shortcodes.zip deleted file mode 100644 index 5852e24e59b42cd7dc5aebf78a4319fde555ae3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4097 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'site')) return; $context = $event->getContext(); + $this->loadLanguage(); if ($context === "com_content.article" || $context === "com_content.featured") { $event->addResult(Text::_('PLG_CONTENT_SHORTCODES_PROCESSED')); From 913c6da2fe6f6f2b139c836eea2f69eeed5d930d Mon Sep 17 00:00:00 2001 From: Robbie Jackson Date: Wed, 10 Dec 2025 18:51:32 +0000 Subject: [PATCH 4/5] changes duplicated to 6.0 and 5.4, and minor fixes to 4.4 --- .../plugins/basic-content-plugin.md | 2 +- .../plugins/plugin-events/installer.md | 32 +- .../plugins/_assets/plg_shortcodes.zip | Bin 3998 -> 0 bytes .../plugins/_assets/shortcodes.jpg | Bin 17135 -> 0 bytes .../plugins/advanced-features.md | 38 +++ .../plugins/basic-content-plugin.md | 103 +++--- .../plugins/how-plugins-work.md | 14 + .../plugins/joomla-4-and-5-changes.md | 284 ---------------- .../plugins/methods-and-arguments.md | 307 ++++++++++++++++++ .../plugins/plugin-events/application.md | 42 +-- .../plugins/plugin-events/content.md | 2 +- .../plugins/plugin-events/index.md | 2 - .../plugins/plugin-events/installer.md | 48 +-- .../plugins/plugin-examples/ajax-plugin.md | 10 +- .../plugins/_assets/plg_shortcodes.zip | Bin 3998 -> 0 bytes .../plugins/_assets/shortcodes.jpg | Bin 17135 -> 0 bytes .../plugins/advanced-features.md | 38 +++ .../plugins/basic-content-plugin.md | 103 +++--- .../plugins/how-plugins-work.md | 14 + .../plugins/joomla-4-and-5-changes.md | 284 ---------------- .../plugins/methods-and-arguments.md | 307 ++++++++++++++++++ .../plugins/plugin-events/application.md | 42 +-- .../plugins/plugin-events/content.md | 2 +- .../plugins/plugin-events/index.md | 2 - .../plugins/plugin-events/installer.md | 48 +-- .../plugins/plugin-examples/ajax-plugin.md | 12 +- 26 files changed, 897 insertions(+), 839 deletions(-) delete mode 100644 versioned_docs/version-5.4/building-extensions/plugins/_assets/plg_shortcodes.zip delete mode 100644 versioned_docs/version-5.4/building-extensions/plugins/_assets/shortcodes.jpg create mode 100644 versioned_docs/version-5.4/building-extensions/plugins/advanced-features.md delete mode 100644 versioned_docs/version-5.4/building-extensions/plugins/joomla-4-and-5-changes.md create mode 100644 versioned_docs/version-5.4/building-extensions/plugins/methods-and-arguments.md delete mode 100644 versioned_docs/version-6.0/building-extensions/plugins/_assets/plg_shortcodes.zip delete mode 100644 versioned_docs/version-6.0/building-extensions/plugins/_assets/shortcodes.jpg create mode 100644 versioned_docs/version-6.0/building-extensions/plugins/advanced-features.md delete mode 100644 versioned_docs/version-6.0/building-extensions/plugins/joomla-4-and-5-changes.md create mode 100644 versioned_docs/version-6.0/building-extensions/plugins/methods-and-arguments.md diff --git a/docs/building-extensions/plugins/basic-content-plugin.md b/docs/building-extensions/plugins/basic-content-plugin.md index 2c48804a..243949b5 100644 --- a/docs/building-extensions/plugins/basic-content-plugin.md +++ b/docs/building-extensions/plugins/basic-content-plugin.md @@ -20,7 +20,7 @@ In addition, the plugin demonstrates the use of: - returning a value from a plugin method - through the use of the `onContentAfterTitle` event. The plugin code adds some text after the article title. The diagram below lists the plugin files to write, or you can download the plugin -from the Joomla Manual Examples repo [shortcodes plugin](https://github.com/joomla/manual-examples/tree/main/plugin-example-shortcode). +from the Joomla Manual Examples repository [shortcodes plugin](https://github.com/joomla/manual-examples/tree/main/plugin-example-shortcode). ``` plg_shortcodes diff --git a/versioned_docs/version-4.4/building-extensions/plugins/plugin-events/installer.md b/versioned_docs/version-4.4/building-extensions/plugins/plugin-events/installer.md index 45449602..43ca57da 100644 --- a/versioned_docs/version-4.4/building-extensions/plugins/plugin-events/installer.md +++ b/versioned_docs/version-4.4/building-extensions/plugins/plugin-events/installer.md @@ -75,17 +75,7 @@ This event is triggered at the beginning of an installation operation. The event class `\Joomla\CMS\Event\Installer\BeforeInstallationEvent` has the following arguments: -- **`subject`** - This will be the FQN of the install model class Joomla\Component\Installer\Administrator\Model\InstallModel - -Note that there is no getter for the subject argument. Instead you should use: - -```php -public function onInstallerBeforeInstallation(\Joomla\CMS\Event\Installer\BeforeInstallationEvent $event): void -{ - $args = $event->getArguments(); - $subject = $args['subject']; -} -``` +- **`model`** - This will be the instance of the install model class Joomla\Component\Installer\Administrator\Model\InstallModel - **`package`** - This is null. @@ -111,18 +101,10 @@ but before Joomla has started to process the extension's installation files. The event class `\Joomla\CMS\Event\Installer\BeforeInstallerEvent` has the following arguments: -- **`subject`** - This will be the FQN of the install model class Joomla\Component\Installer\Administrator\Model\InstallModel +- **`model`** - This will be the instance of the install model class Joomla\Component\Installer\Administrator\Model\InstallModel Note that there is no getter for the subject argument. Instead you should use: -```php -public function onInstallerBeforeInstaller(\Joomla\CMS\Event\Installer\BeforeInstallerEvent $event): void -{ - $args = $event->getArguments(); - $subject = $args['subject']; -} -``` - - **`package`** - This is an array of 4 elements, which in the case of uploading a zip file will contain: - extractdir - the directory into which the zip file has been extracted @@ -154,18 +136,10 @@ This event is triggered after extension has been installed. The event class `\Joomla\CMS\Event\Installer\AfterInstallerEvent` has the following arguments: -- **`subject`** - This will be the FQN of the install model class Joomla\Component\Installer\Administrator\Model\InstallModel +- **`model`** - This will be the instance of the install model class Joomla\Component\Installer\Administrator\Model\InstallModel Note that there is no getter for the subject argument. Instead you should use: -```php -public function onInstallerAfterInstaller(\Joomla\CMS\Event\Installer\AfterInstallerEvent $event): void -{ - $args = $event->getArguments(); - $subject = $args['subject']; -} -``` - - **`package`** - The package which has been installed, in the form described in onInstallerBeforeInstaller above. - **`installer`** - The installer class - Joomla sets this to "Joomla\CMS\Installer\Installer" diff --git a/versioned_docs/version-5.4/building-extensions/plugins/_assets/plg_shortcodes.zip b/versioned_docs/version-5.4/building-extensions/plugins/_assets/plg_shortcodes.zip deleted file mode 100644 index 1950497b9cdae9d6953bfe6fcf14e219e4d40e8f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3998 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 diff --git a/versioned_docs/version-5.4/building-extensions/plugins/_assets/shortcodes.jpg b/versioned_docs/version-5.4/building-extensions/plugins/_assets/shortcodes.jpg deleted file mode 100644 index ac7211d80781bc9453283983506abfdea1e1c360..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17135 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
      >8fL3O3$IJ>=|IsYw09VKi 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, ]` + +```php +public static function getSubscribedEvents(): array +{ + return [ + 'onContentPrepare' => ['myContentPrepareMethod', \Joomla\Event\Priority::HIGH], + 'onContentAfterTitle' => ['myContentAfterTitleMethod', \Joomla\Event\Priority::MIN], + ]; +} +``` + +See the other values available in libraries/vendor/joomla/event/src/Priority.php. +Use with caution though, as this will override any priority which the site administrator may want to set through ordering the plugins. + +## Stop Event Propagation + +To stop the propagation of an event to other plugins you can call + +```php +$event->stopPropagation(); +``` diff --git a/versioned_docs/version-5.4/building-extensions/plugins/basic-content-plugin.md b/versioned_docs/version-5.4/building-extensions/plugins/basic-content-plugin.md index b443ad34..243949b5 100644 --- a/versioned_docs/version-5.4/building-extensions/plugins/basic-content-plugin.md +++ b/versioned_docs/version-5.4/building-extensions/plugins/basic-content-plugin.md @@ -19,14 +19,25 @@ In addition, the plugin demonstrates the use of: - language constants - both in the manifest file and in the plugin code - returning a value from a plugin method - through the use of the `onContentAfterTitle` event. The plugin code adds some text after the article title. -You can test this plugin on both Joomla 4 and Joomla 5 instances, to see the differences in obtaining the parameters and returning the result (as described in [Joomla 4 and 5 changes](joomla-4-and-5-changes.md)). +The diagram below lists the plugin files to write, or you can download the plugin +from the Joomla Manual Examples repository [shortcodes plugin](https://github.com/joomla/manual-examples/tree/main/plugin-example-shortcode). -![Shortcodes plugin files](_assets/shortcodes.jpg "Shortcodes plugin files") - -The diagram shows the plugin files to write, or you can download a zip file of the plugin from [shortcodes plugin download](_assets/plg_shortcodes.zip). +``` +plg_shortcodes + ├─── language + │ └─── en-GB + │ ├─── plg_content_shortcodes.ini + │ └─── plg_content_shortcodes.sys.ini + ├─── services + │ └─── provider.php + ├─── src + │ └─── Extension + │ └─── Shortcode.php + └─── shortcodes.xml +``` ## Manifest File -For general information on manifest files see [Manifest Files](https://docs.joomla.org/Manifest_files). +For general information on manifest files see [Manifest Files](../install-update/installation/manifest.md). ```xml title="plg_shortcodes/shortcodes.xml" @@ -36,7 +47,7 @@ For general information on manifest files see [Manifest Files](https://docs.joom PLG_CONTENT_SHORTCODES_DESCRIPTION Me Today - (C) 2024 Open Source Matters, Inc. + (C) 2025 Open Source Matters, Inc. GNU General Public License version 2 or later My\Plugin\Content\Shortcodes @@ -58,7 +69,7 @@ Joomla is quite particular when it comes to plugin manifest files, and it's easy ``` -Previous sections described plugin types as 'content', 'system', etc but here the `type` is "plugin" (as it's the type of the extension) and the `group` refers to the plugin type. +Here the `type` is "plugin" (as it's the type of the extension) and the `group` refers to the plugin type. ### Language constants @@ -156,7 +167,7 @@ This is pretty much boilerplate code for obtaining your plugin from the Dependen use My\Plugin\Content\Shortcodes\Extension\Shortcode; ``` -Ensure that this aligns with your `` in the manifest file and your `namespace` statement and class name in the Extension class file. . +Ensure that this aligns with your `` in the manifest file and your `namespace` statement and class name in the Extension class file. ```php $config = (array) PluginHelper::getPlugin('content', 'shortcodes'); @@ -172,16 +183,7 @@ Ensure that this matches your class in your `src/Extension` directory. ### Extension Class This is the main code of the plugin. Hopefully the comments in the code explain what is going on. - -As explained in [Joomla 4 and 5 changes](./joomla-4-and-5-changes.md), code which triggers the Events can use a `GenericEvent` or a concrete Event, eg `ContentPrepareEvent`. In both these cases you can get the arguments using - -```php -[$context, $article, $params, $page] = array_values($event->getArguments()); -``` - -but you have to check in your code how to return the result. - -Using this approach means that you don't need to change your plugin code if the code which is triggering the event changes from using a generic Event class to a concrete event class. +For further details see the page on [Plugin Methods and Arguments](./methods-and-arguments.md). ```php title="plg_shortcodes/src/Extension/Shortcode.php" getApplication(); + + // The line below restricts the functionality to the front-end site (ie not admin/api/console job) // You may not want this, so you need to consider this in your own plugins - if (!$this->getApplication()->isClient('site')) { + if (!$app->isClient('site')) { return; } - - // use this format to get the arguments for both Joomla 4 and Joomla 5 - // In Joomla 4 a generic Event is passed - // In Joomla 5 a concrete ContentPrepareEvent is passed - [$context, $article, $params, $page] = array_values($event->getArguments()); + + // Find out if this event relates to an article or something else (eg contact, user) + $context = $event->getContext(); if ($context !== "com_content.article" && $context !== "com_content.featured") return; + // If we've reached here then it's an article - get the 'text' property + $article = $event->getItem(); $text = $article->text; // text of the article - $config = Factory::getApplication()->getConfig()->toArray(); // config params as an array + + $config = $app->getConfig()->toArray(); // config params as an array // (we can't do a foreach over the config params as a Registry because they're protected) // the following is just code to replace {configname} with the parameter value @@ -269,22 +274,22 @@ class Shortcode extends CMSPlugin implements SubscriberInterface $article->text = $text; } - public function addShortcodeSubtitle(Event $event) + public function addShortcodeSubtitle(AfterTitleEvent $event): void { - if (!$this->getApplication()->isClient('site')) { - return; - } - [$context, $article, $params, $page] = array_values($event->getArguments()); - if ($context !== "com_content.article" && $context !== "com_content.featured") return; - - $eventType = method_exists($event, 'getContext') ? "concrete event class" : "generic event class"; + /* This function adds a subtitle to a page on the site front end: + * "Processed for shortcodes" - if the page is an article + * "Not processed for shortcodes" - if the page is a contact, etc. + */ + + if (!$this->getApplication()->isClient('site')) return; - if ($event instanceof ResultAwareInterface) { - $event->addResult("{$eventType} via addResult"); + $context = $event->getContext(); + $this->loadLanguage(); + if ($context === "com_content.article" || $context === "com_content.featured") + { + $event->addResult(Text::_('PLG_CONTENT_SHORTCODES_PROCESSED')); } else { - $result = $event->getArgument('result') ?? []; - $result[] = "{$eventType} via setArgument"; - $event->setArgument('result', $result); + $event->addResult(Text::_('PLG_CONTENT_SHORTCODES_NOT_PROCESSED')); } } } @@ -302,6 +307,8 @@ Language constants which are used in the plugin code: ```php title="plg_shortcodes/language/en-GB/plg_content_shortcodes.ini" PLG_CONTENT_SHORTCODES_NO_MATCH="Error: no match for shortcode found" +PLG_CONTENT_SHORTCODES_PROCESSED="Processed for shortcodes" +PLG_CONTENT_SHORTCODES_NOT_PROCESSED="Not processed for shortcodes" ``` ## Installation @@ -313,4 +320,6 @@ On my Joomla instance {sitename} the default editor is {editor}. ``` Then navigate to a Joomla site menuitem which displays this article, either as a single article or as one of the featured articles. -The code should work on both Joomla 4 and Joomla 5, and should also display after the article title information about the type of event class used, and how the value is returned. +Include a non-existent configuration parameter in your shortcode to display the error text. + +Also compare the subtitle shown for a page displaying an article with a page displaying a contact. \ No newline at end of file diff --git a/versioned_docs/version-5.4/building-extensions/plugins/how-plugins-work.md b/versioned_docs/version-5.4/building-extensions/plugins/how-plugins-work.md index 8c076941..27c8f159 100644 --- a/versioned_docs/version-5.4/building-extensions/plugins/how-plugins-work.md +++ b/versioned_docs/version-5.4/building-extensions/plugins/how-plugins-work.md @@ -2,6 +2,12 @@ title: How Plugins Work sidebar_position: 1 --- + +How Plugins Work +================ + +## Overview + ![Plugins Overview](_assets/plugin-overview.jpg "Plugins Overview") The diagram shows how Joomla plugins work. @@ -20,6 +26,8 @@ The process of involving the plugins comprises 2 steps: 1. Import all plugins of a given type 2. Trigger an event +## Importing a Plugin + Importing a plugin type is implemented by the code: ```php PluginHelper::importPlugin($pluginType, …); @@ -36,12 +44,16 @@ Importing a plugin type involves: Splitting the plugins by plugin type in this way makes Joomla more performant - it doesn't need to process plugins which aren't going to be interested in the event which will be triggered next. +## Triggering an Event + The second step is triggering an event via a call to the event dispatcher. Joomla looks through its store of `Listeners` to determine which plugins have subscribed to that event type. It then calls the associated method of the subscribing plugin, and passes the event data to it, often allowing that data to be modified by the plugin code. Each plugin which has subscribed to that event is called in turn (based on a priority scheme), and any results returned by the plugins are collated into an array associated with the event. As an example, after Joomla is initialised system plugins are imported and the event `onAfterInitialise` is triggered. The "Remember Me" plugin (in plugins/system/remember) receives notification of this, and can log in a user who has previously checked the "Remember Me" checkbox on the login form. +## Subsequent Events + Once a plugin has been imported and its subscriptions logged in the `Listeners` data store, then it continues to receive notifications for all the events it has subscribed to. For example, consider the following scenario: - system plugins are imported, and the `onAfterInitialise` event is triggered - some time later content plugins are imported, and the `onContentPrepare` event is triggered. @@ -50,6 +62,8 @@ The system plugins imported in the first step can subscribe to the `onContentPre You should also be aware that unlike components and modules, there aren't different site plugins and administrator plugins. The plugins which you install are run for all the different contexts - site, administrator and API applications - so it's a good idea to in your plugins to check the application context. +## Sequence Diagram + For those who wish a more detailed picture of how plugins work, see the sequence diagram below. ```mermaid diff --git a/versioned_docs/version-5.4/building-extensions/plugins/joomla-4-and-5-changes.md b/versioned_docs/version-5.4/building-extensions/plugins/joomla-4-and-5-changes.md deleted file mode 100644 index 76af7ef9..00000000 --- a/versioned_docs/version-5.4/building-extensions/plugins/joomla-4-and-5-changes.md +++ /dev/null @@ -1,284 +0,0 @@ ---- -title: Joomla 4 and 5 Changes -sidebar_position: 2 ---- - -Joomla 4 and 5 Changes -====================== - -## Key changes -In Joomla 4 a number of significant changes began to be introduced to the plugins area: -1. Plugins were instantiated via the Dependency Injection Container, through the `services/provider.php` file. Previously plugins were run via the plugin's main php file. -2. Plugins subscribed to the events which they wanted to get notified about. Previously you wrote a method whose name matched the name of the event, and Joomla used PHP reflection classes to determine when your method got called. With the subscription mechanism Joomla checks if your plugin implements `Joomla\Event\SubscriberInterface` and if so calls `getSubscribedEvents` to which the plugin returns the event types it wants to handle, and the method which Joomla should call: -```php -public static function getSubscribedEvents(): array -{ - return [ - 'onContentPrepare' => 'myContentPrepareMethod', - 'onContentAfterTitle' => 'myContentAfterTitleMethod', - ]; -} -``` -3. Events were passed to plugins as Joomla Event objects. Previously each method associated with an event had parameters passed which were specific to that event type. For example, the method called when `onContentPrepare` was triggered was: -```php -public function onContentPrepare($context, &$row, $params, $page = 0) -``` -In Joomla 3 "events" basically comprised -- a string with the event name eg "onContentPrepare", and -- associated parameters, which were passed to the `onContentPrepare` method. - -The aim of the Joomla team has been to replace these with event classes, with each type of event having its own concrete event class. For example, for the `onContentPrepare` event there would be a `ContentPrepareEvent` class, with properties `$context`, `$row`, `$params` and `$page`. However, the process of replacing all the current code has been spread over a number of Joomla releases, some being done in Joomla 4 and some in Joomla 5. - -To enable plugins to use Event classes before the full concrete event class could be implemented a temporary solution was adopted, that is, to provide a `GenericEvent` class, which meant that plugins could be developed which used the Event parameter in their methods, rather than the list of parameters. - -**It's important to understand that the way you obtain the parameters of your plugin method, and the way which you provide your return value is different depending on whether a GenericEvent or a concrete class is used.** - -This is what we're now going to look at in detail. - -## Joomla 3 / 4 / 5 comparison -Let's take the example of `onContentPrepare` and consider how it has changed through these releases. - -### Joomla 3 -In Joomla 3 the event was triggered in `com_content` using -```php -$dispatcher->trigger('onContentPrepare', array ('com_content.article', &$item, &$item->params, $offset)); -``` -This resulted in a call to your plugin method: -```php -public function onContentPrepare($context, &$row, $params, $page = 0) -``` -and Joomla found this method by using the reflection class of your plugin class. - -`onContentPrepare` doesn't take any return value, but other plugins such as `onContentAfterTitle` do. To specify the return value you simply did -```php -return $value; -``` - -The Joomla team have provided backward compatibility for plugins through releases 4 and 5, so this mechanism will still work. However it will likely disappear in Joomla 6, so you should not write any new plugins this way. - -### Joomla 4 -In Joomla 4 the event is triggered using: -```php -$this->dispatchEvent(new Event('onContentPrepare', ['com_content.article', &$item, &$item->params, $offset])); -``` -This creates a `GenericEvent` which has Argument fields for the parameters above, which can be obtained via the `getArguments` method (using PHP array destructuring): -```php -[$context, $article, $params, $page] = array_values($event->getArguments()); -``` -The `array_values` is not really necessary here, but we'll see the reason for it shortly. - -For `GenericEvent` the `result` is an array which is held within the `GenericEvent` class, and you have to add any return value from your plugin into this array. So to return a value from a plugin method using `GenericEvent` you use: -```php -$result = $event->getArgument('result') ?: []; // get the result argument from GenericEvent -$result[] = $value; // add your return value into the array -$event->setArgument('result', $result); // write back the updated result into the GenericEvent instance -``` - -### Joomla 5 -In Joomla 5 the event is triggered using: -```php -$dispatcher->dispatch('onContentPrepare', new Content\ContentPrepareEvent('onContentPrepare', $contentEventArguments)); -``` -Here the concrete event class `ContentPrepareEvent` (from libraries/src/Event/Content/ContentPrepareEvent.php) is used. Such events may have specific getter methods (eg `getContext`), but you can still get the parameters using: -```php -[$context, $article, $params, $page] = array_values($event->getArguments()); -``` -Here the `array_values` is necessary, so if you use this to obtain the parameters it will work for both `GenericEvent` and concrete Event classes. - -To return a value your plugin should use `$event->addResult($value)`, but you can check if this method exists by -```php -use Joomla\CMS\Event\Result\ResultAwareInterface; -... - if ($event instanceof ResultAwareInterface) { - $event->addResult($value); - return; - } else { - // use GenericEvent approach - } -``` - -**If you're developing a developing a plugin to handle a `GenericEvent` then it's crucial to use the above mechanisms to avoid the plugin failing whenever the triggering code moves to using a concrete Event class.** - -## Summary - Accessing Event Arguments - -### Concrete Event Class - -You can use this approach if under libraries/src/Event there is a Joomla event class (known as a "concrete" event class) which is specific to the plugin event group. (Only a limited set is available for Joomla 4). - -```php -use Joomla\CMS\Plugin\CMSPlugin; -use Joomla\Event\SubscriberInterface; -use Joomla\CMS\Event\Content\ContentPrepareEvent; - -class MyPlugin extends CMSPlugin implements SubscriberInterface -{ - public static function getSubscribedEvents(): array - { - return [ - 'onContentPrepare' => 'myOnContentPrepare', - ]; - } - - public function myOnContentPrepare(ContentPrepareEvent $event) - { - $context = $event->getContext(); - $item = $event->getItem(); - $params = $event->getParams(); - $page = $event->getPage(); - // ... - } -``` - -Here in the getter call you must use the correct name for the argument, and in the Joomla manual documentation the correct name is always specified. - -You can also find the getter methods in the API documentation for the event class, for example [Event/Content/ContentPrepareEvent methods](cms-api://classes/Joomla-CMS-Event-Content-ContentPrepareEvent.html). - -To use an event class your plugin class must implement \Joomla\Event\SubscriberInterface and provide the `getSubscribedEvents` function. Your plugin listener function must then have just the single `$event` parameter. - -### Generic Event Class - -Use this approach after Joomla 4.0 if you wish to use event classes, but a concrete event class isn't available for your target Joomla version. - -```php -use Joomla\CMS\Plugin\CMSPlugin; -use Joomla\Event\SubscriberInterface; -use Joomla\Event\Event; - -class MyPlugin extends CMSPlugin implements SubscriberInterface -{ - public static function getSubscribedEvents(): array - { - return [ - 'onContentPrepare' => 'myOnContentPrepare', - ]; - } - - public function myOnContentPrepare(Event $event) - { - [$context, $item, $params, $page] = array_values($event->getArguments()); - ... - } -``` - -This approach also works for concrete event classes. - -It depends on the order of the arguments in the event class, and the correct order is always given in the documentation. - -There is a commitment from the Joomla team to preserve the same order within the event arguments as within the order of the parameters of the legacy method, but this will be removed in Joomla 6. This is implemented via the variable `$legacyArgumentsOrder`, eg in Joomla\CMS\Event\Content\ContentPrepareEvent (in libraries/src/Event/Content/ContentPrepareEvent.php): - -```php -class ContentPrepareEvent extends ContentEvent -{ - protected $legacyArgumentsOrder = ['context', 'subject', 'params', 'page']; -``` - -### ArrayAccess - -The Joomla Event class supports the [PHP ArrayAccess interface](https://www.php.net/manual/en/class.arrayaccess.php). -So you can use this method to obtain the arguments for both generic events and concrete events: - -```php -use Joomla\Event\Event; - - public function myOnContentPrepare(Event $event) - { - $context = $event['context']; - } -``` - -If the 'context' array element / argument doesn't exist then `$event['context']` returns null, -so you can use this to check if an argument exists. - -### Traditional Legacy Method - -You will be using this method if you developed your plugin before Joomla 4.0. In this case, you're probably better to continue this approach until concrete event classes are available for all the events your plugin listens for. - -You shouldn't use this approach for developing a new plugin, as you will have to rework it. - -To use the traditional (legacy) method, you specify a public function which has the same name as the event name, and Joomla uses PHP reflection to find your method. You specify the event parameters in your function signature, for example: - -```php -use Joomla\CMS\Plugin\CMSPlugin; - -class MyPlugin extends CMSPlugin -{ - public function onContentPrepare($context, $item, $params, $page) - { - if ($context == "com_content.article") ... - } -``` - -It doesn't matter what you specify as the names of your function parameters, just on their order in the parameter sequence. -The documentation in the Joomla manual always specifies the correct order of these arguments. - -## Summary - Returning Values - -### Concrete Event Class - -To return a value when using a concrete event class use `$event->addResult()`: - -```php -use Joomla\CMS\Plugin\CMSPlugin; -use Joomla\CMS\Event\Content\AfterTitleEvent; - -class MyPlugin extends CMSPlugin - - public function onContentAfterTitle(AfterTitleEvent $event) - { - ... - $event->addResult($value); - } -``` - -### Generic Event Class - -To return a value when using a generic event class use the following: - -```php -use Joomla\CMS\Plugin\CMSPlugin; -use Joomla\Event\Event; - -class MyPlugin extends CMSPlugin - - public function onContentAfterTitle(Event $event) - { - ... - $result = $event->getArgument('result') ?: []; // get the result argument from GenericEvent - $result[] = $value; // add your return value into the array - $event->setArgument('result', $result); - } -``` - -This approach will also work with concrete event classes. - -### Traditional Legacy Method - -To return a value when using the legacy method use the PHP `return`: - -``` - public function onContentAfterTitle($context, $item, $params, $page) - { - ... - return $value; - } -``` - -## Other New Features -### Priority -To use a plugin priority other than the default, specify this in your response to `getSubscribedEvents` -```php -public static function getSubscribedEvents(): array -{ - return [ - 'onContentPrepare' => ['myContentPrepareMethod', \Joomla\Event\Priority::HIGH], - 'onContentAfterTitle' => ['myContentAfterTitleMethod', \Joomla\Event\Priority::MIN], - ]; -} -``` -See the other values available in libraries/vendor/joomla/event/src/Priority.php. Use with caution though, as this will override any priority which the site administrator may want to set through ordering the plugins. - -### Stop Propagation -To stop the propagation of an event to other plugins you can call -```php -$event->stopPropagation(); -``` diff --git a/versioned_docs/version-5.4/building-extensions/plugins/methods-and-arguments.md b/versioned_docs/version-5.4/building-extensions/plugins/methods-and-arguments.md new file mode 100644 index 00000000..dcc8a259 --- /dev/null +++ b/versioned_docs/version-5.4/building-extensions/plugins/methods-and-arguments.md @@ -0,0 +1,307 @@ +--- +title: Plugin Methods and Arguments +sidebar_position: 2 +--- + +This section describes some general features of plugin code, +and many of these are used in the [plugin tutorial](./basic-content-plugin.md) which follows. + +Plugin Methods and Arguments +============================ + +There are 2 functions which you must implement within your plugin: + +1. The `getSubscribedEvents()` method. You tell Joomla + - which events you want to subscribe to + - the code to run when one of those events is triggered + +2. The handler function which Joomla should call when one of those events is triggered. + +This section describes these functions in detail. + +A note on terminology: you will find that the terms "dispatch", "trigger" and "emit" an event +are used interchangeably in both the documentation and in the Joomla code. + +## getSubscribedEvents method + +In this function you simply return an associative array where + +- the index is the event name +- the value is the name of the function in your plugin which handles that event + +```php +use Joomla\CMS\Plugin\CMSPlugin; +use Joomla\Event\SubscriberInterface; + +class MyPlugin extends CMSPlugin implements SubscriberInterface +{ + public static function getSubscribedEvents(): array + { + return [ + 'onContentPrepare' => 'modifyContent', + 'onContentAfterTitle' => 'addSubtitle', + ]; + } +``` + +It's important that you state that your plugin `implements SubscriberInterface`. + +(Note that you can also set a priority for each event and handler - see [Priority of Events](./advanced-features.md#priority-of-events)). + +## Your handler function + +Here is an example based on the [onContentPrepare event](./plugin-events/content.md#oncontentprepare): + +```php +use \Joomla\CMS\Event\Content\ContentPrepareEvent; + +public function modifyContent(ContentPrepareEvent $event): void +{ + if (!$this->getApplication()->isClient('site')) { + return; + } + $item = $event->getItem(); // preferred way to get the event arguments + $args = $event->getArguments(); // fallback mechanism + $context = $event['context']; // using PHP Array Access + if ($context) { + ... + } +} +``` + +The subsections below provide some detailed descriptions. + +### Event Class + +Each event has its own PHP class, and you can find these in libraries/src/Event. +Some event classes are in this directory, but most are in subdirectories categorised by the area of Joomla where that event is triggered. + +The name of the event is often similar to the class name, +eg the event class \Joomla\CMS\Event\Content\ContentPrepareEvent is associated with the event onContentPrepare. +This is an event which is of type "content", in the sense that plugins of type "content" are imported prior to it being triggered. +However, there isn't a "type" field associated with the event instance which defines this plugin type (`$event` doesn't have a "type" property). + +There is a mapping between many event names and event classes in libraries/src/Event/CoreEventAware.php. + +You may be wondering: "How do I find out which event to use to achieve what I want?" + +If a suitable event isn't documented in this manual, and a response from AI doesn't give you a useful answer, +then there are a few options to consider: + +- look through the event classes below libraries/src/Event or in libraries/vendor/composer/autoload_classmap.php +- check for the mapping to event name in libraries/src/Event/CoreEventAware.php +- check the outdated [Plugin Events documentation](https://docs.joomla.org/Plugin/Events) +- search the Joomla code for the possible event names and event class names. + +### Checking the Client + +It's important to remember that plugins are run whenever Joomla is run: + +- the front-end site +- the back-end administrator +- using the Joomla API +- running a console job + +So in your event handler code you should generally check the context using something like: + +```php +if (!$this->getApplication()->isClient('site')) { + return; +} +``` + +The [getApplication](#getapplication) function is an inherited method, as described below. + +### Getter Functions + +Each Event instance has a number of arguments which are documented in the detailed plugin event pages +(and sometimes in the source code of the event class). + +You should use the event getter functions to obtain the event arguments, for example + +```php +$context = $event->getContext(); +$item = $event->getItem(); +$params = $event->getParams(); +$page = $event->getPage(); +``` + +The getter function matches the name of the argument in the documentation. + +`$event->getName()` returns the name of the event. + +### Arguments + +Occasionally you may find that there isn't a getter function for a particular argument. +In this case you can obtain the event arguments by: + +```php +$args = $event->getArguments(); +``` + +The returned value is an associative array mapping the arguments to their values, eg: + +```php +["context" => "com_content.article", "subject" => , + "params" =>
      , "page" => 0] +``` + +(Note that the array element index doesn't always match the argument in the documentation pages. +For example, for ContentPrepareEvent the 'item' argument is given by the "subject" element in the array.) + +### Array Access + +The Joomla Event class supports the [PHP ArrayAccess interface](https://www.php.net/manual/en/class.arrayaccess.php). +So you can use this method to obtain the arguments, and you may find this useful in the case where an explicit getter function is not available. + +```php +use \Joomla\CMS\Event\Content\ContentPrepareEvent; + + public function modifyContent(ContentPrepareEvent $event): void + { + $context = $event['context']; + if ($context) { + ... + } + } +``` + +If the 'context' array element / argument doesn't exist then `$event['context']` returns null, +so you can use this to check if an argument exists. No PHP warning is generated if the array index is not present. + +### Updating arguments and returning results + +The documentation will state whether any arguments are modifiable, or whether a result should be returned. + +For example, ContentPrepareEvent passes a 'subject' argument (available via `getItem()`) which you can modify. +Depending upon the 'context' argument this item may be an article, a contact, a user, etc., +and you can set properties of this object. + +```php +$context = $event->getContext(); +if ($context === "com_content.article") { + $article = $event->getItem(); + $article->text = $article->text . " [end of text]"; // Appends " [end of text]" to the end of the article +} +``` + +(In this case the appended text would be included in the HTTP response, but not saved to the database). + +If the event allows a result to be returned then you should use this approach: + +```php +use Joomla\CMS\Event\Content\AfterTitleEvent; + + public function onContentAfterTitle(AfterTitleEvent $event) + { + ... + $event->addResult($value); + } +``` + +Sometimes the event provides a specific function which allows you to update an entity. +For example, the [onInstallerBeforeInstaller](./plugin-events/installer.md#oninstallerbeforeinstaller) event +allows the plugin to change the package which is being installed: + +```php +public function onInstallerBeforeInstaller(\Joomla\CMS\Event\Installer\BeforeInstallerEvent $event): void +{ + $newpkg = array("dir" => $pathToExtensionDirectory, "type" => "plugin", "extractdir" => null, "packagefile" => null); + $event->updatePackage($newpkg); +} +``` + +## Methods available to Plugins + +Inside your plugin you have access to several methods. + +### getApplication + +- `$this->getApplication()` returns the Application instance. (This method is inherited from CMSPlugin). +However, you need to inject this dependency in your plugin's services/provider.php file. + +```php + $plugin = new MyPlugin($subject, $config); + $app = Joomla\CMS\Factory::getApplication(); + $plugin->setApplication($app); +``` + +From the `$app` instance you can find other details +such as the global configuration (getConfig), current user (getIdentity), language (getLanguage), document (getDocument), etc. + +### loadLanguage + +- `$this->loadLanguage()` (also inherited) will load your plugin's language constants. +You have to load them before using them in your code. + +Alternatively you can set within your plugin class: + +```php +protected $autoloadLanguage = true; +``` + +and Joomla will load your plugin's language constants for you. + +:::warning + This feature has a negative effect on performance, as it means that your language constants are processed even when they're not used. + You should always load them manually. +::: + +### Injected Dependencies + +If your plugin needs access to other Joomla class instances which are in the main Dependency Injection container then you should follow this pattern: + +- in your services/provider.php file get the instance out of the DI container and pass it into your plugin's setter method + +- in your plugin use your getter method to retrieve it. + +- include these getter and setter methods in your plugin by `use` + the appropriate trait + +For example, to include the database object do this in your services/provider.php file: + +```php + $plugin = new MyPlugin( (array) PluginHelper::getPlugin('content', 'myplugin') ); + $plugin->setDatabase($container->get(DatabaseInterface::class)); +``` + +and include this in your plugin file: + +```php +use Joomla\CMS\Plugin\CMSPlugin; +use Joomla\Event\SubscriberInterface; +use Joomla\Database\DatabaseAwareTrait; + +class MyPlugin extends CMSPlugin implements SubscriberInterface +{ + use DatabaseAwareTrait; + + public function modifyContent(ContentPrepareEvent $event): void + { + $db = $this->getDatabase(); + } +} +``` + +As another example, if your plugin sends mail then you can set the MailerFactory as a dependency. + +```php title="services/provider.php" + $plugin = new MyPlugin( (array) PluginHelper::getPlugin('content', 'myplugin') ); + $plugin->setMailerFactory($container->get(MailerFactoryInterface::class)); +``` + +```php title="plugin file" +use Joomla\CMS\Mail\MailerFactoryAwareTrait; + +class MyPlugin extends CMSPlugin implements SubscriberInterface +{ + use MailerFactoryAwareTrait; + + public function modifyContent(ContentPrepareEvent $event): void + { + $mailer = $this->getMailerFactory()->createMailer(); + } +} +``` + +These traits are scattered throughout the Joomla libraries directories, +but you can find them by searching for filenames which match `*AwareTrait.php`. \ No newline at end of file diff --git a/versioned_docs/version-5.4/building-extensions/plugins/plugin-events/application.md b/versioned_docs/version-5.4/building-extensions/plugins/plugin-events/application.md index 9e96aae2..2c9c5249 100644 --- a/versioned_docs/version-5.4/building-extensions/plugins/plugin-events/application.md +++ b/versioned_docs/version-5.4/building-extensions/plugins/plugin-events/application.md @@ -54,7 +54,7 @@ If you would like further information then you may find it helpful to watch the ## Application Events overview The application event classes can be found in libraries/src/Event/Application. -In general they have a single parameter `subject` which is set to the Application instance, +In general they have a single parameter `application` which is set to the Application instance, and the getter method for this parameter is `getApplication`, for example: ```php @@ -133,7 +133,7 @@ public function onAfterInitialise(AfterInitialiseEvent $event): void The event class \Joomla\CMS\Event\Application\AfterInitialiseEvent has the following arguments: -- **`subject`** - the Application instance, available via `$event->getApplication()` +- **`application`** - the Application instance, available via `$event->getApplication()` ### Return Value @@ -151,7 +151,7 @@ You can use this event, for example, to tidy up aspects after the routing functi The event class \Joomla\CMS\Event\Application\AfterRouteEvent has the following arguments: -- **`subject`** - the Application instance, available via `$event->getApplication()` +- **`application`** - the Application instance, available via `$event->getApplication()` ### Return Value @@ -169,9 +169,9 @@ You can use this event, to set aspects of the Document. The event class \Joomla\CMS\Event\Application\AfterInitialiseDocumentEvent has the following arguments: -- **`subject`** - the Application instance, available via `$event->getApplication()` +- **`application`** - the Application instance, available via `$event->getApplication()` -- **`document`** - The Document. You can use the getter method `$doc = $event->getDocument();` to get this. +- **`document`** - The Document, available via `$event->getDocument()` ### Return Value @@ -187,7 +187,7 @@ This event is triggered after Joomla has run the main component and buffered its The event class \Joomla\CMS\Event\Application\AfterDispatchEvent has the following arguments: -- **`subject`** - the Application instance, available via `$event->getApplication()` +- **`application`** - the Application instance, available via `$event->getApplication()` ### Return Value @@ -204,7 +204,7 @@ to fill in the `` tags. The event class \Joomla\CMS\Event\Application\BeforeRenderEvent has the following arguments: -- **`subject`** - the Application instance, available via `$event->getApplication()` +- **`application`** - the Application instance, available via `$event->getApplication()` ### Return Value @@ -222,9 +222,9 @@ You can use this to include other items within the ``, via the [Web Asset The event class \Joomla\CMS\Event\Application\BeforeCompileHeadEvent has the following arguments: -- **`subject`** - the Application instance, available via `$event->getApplication()` +- **`application`** - the Application instance, available via `$event->getApplication()` -- **`document`** - The Document. You can use the getter method `$doc = $event->getDocument();` to get this. +- **`document`** - The Document, available via `$event->getDocument()` ### Return Value @@ -252,7 +252,7 @@ public function onAfterRender(AfterRenderEvent $event): void The event class \Joomla\CMS\Event\Application\AfterRenderEvent has the following arguments: -- **`subject`** - the Application instance, available via `$event->getApplication()` +- **`application`** - the Application instance, available via `$event->getApplication()` ### Return Value @@ -268,7 +268,7 @@ This event is triggered just before Joomla sends the HTTP response. The event class \Joomla\CMS\Event\Application\BeforeRespondEvent has the following arguments: -- **`subject`** - the Application instance, available via `$event->getApplication()` +- **`application`** - the Application instance, available via `$event->getApplication()` ### Return Value @@ -284,7 +284,7 @@ This event is triggered just after Joomla sends the HTTP response. The event class \Joomla\CMS\Event\Application\AfterRespondEvent has the following arguments: -- **`subject`** - the Application instance, available via `$event->getApplication()` +- **`application`** - the Application instance, available via `$event->getApplication()` ### Return Value @@ -304,7 +304,7 @@ which could be used by the extension's `boot()` method (which is run when the ex The event class \Joomla\CMS\Event\BeforeExtensionBootEvent has the following arguments: -- **`subject`** - the Application instance, but note that there is no getter function for this. Instead you can use: +- **`application`** - the Application instance, but note that there is no getter function for this. Instead you can use: ```php use \Joomla\CMS\Event\BeforeExtensionBootEvent; @@ -315,14 +315,9 @@ public function onBeforeExtensionBoot(BeforeExtensionBootEvent $event): void } ``` -- **`type`** - This is the type of extension being booted, +- **`extensionType`** - This is the type of extension being booted, identified by the interface it supports, eg "Joomla\CMS\Extension\PluginInterface". - -Note that the getter function for this argument is getExtensionType rather than getType: - -```php -$type = $event->getExtensionType(); -``` +Available via `$event->getExtensionType()` - **`extensionName`** - The extension name, available via `$event->getExtensionName()`: @@ -361,7 +356,7 @@ then com_content will run your MVC classes instead of its own. The event class \Joomla\CMS\Event\AfterExtensionBootEvent has the following arguments: -- **`subject`** - the Application instance, but note that there is no getter function for this. Instead you can use: +- **`application`** - the Application instance, but note that there is no getter function for this. Instead you can use: ```php use \Joomla\CMS\Event\AfterExtensionBootEvent; @@ -372,10 +367,9 @@ public function onBeforeExtensionBoot(AfterExtensionBootEvent $event): void } ``` -- **`type`** - This is the type of extension booted, +- **`extensionType`** - This is the type of extension being booted, identified by the interface it supports, eg "Joomla\CMS\Extension\PluginInterface". - -Note that the getter function for this argument is getExtensionType rather than getType: +Available via `$event->getExtensionType()` ```php $type = $event->getExtensionType(); diff --git a/versioned_docs/version-5.4/building-extensions/plugins/plugin-events/content.md b/versioned_docs/version-5.4/building-extensions/plugins/plugin-events/content.md index a37c1f55..3b04e6a4 100644 --- a/versioned_docs/version-5.4/building-extensions/plugins/plugin-events/content.md +++ b/versioned_docs/version-5.4/building-extensions/plugins/plugin-events/content.md @@ -15,7 +15,7 @@ They are triggered during the process of: The sections below give a brief description of each content event, what the event parameters / arguments are, and any examples of their use which are available in the Joomla Manual. -For background on Joomla transitioning to using classes for events see [Joomla 4 and 5 changes](../joomla-4-and-5-changes.md), where you can also find explanations for [accessing the arguments](../joomla-4-and-5-changes.md#summary---accessing-event-arguments) and [returning values](../joomla-4-and-5-changes.md#summary---returning-values). +For background on obtaining arguments and returning results see [Plugin Methods and Arguments](../methods-and-arguments.md). ## onContentPrepare diff --git a/versioned_docs/version-5.4/building-extensions/plugins/plugin-events/index.md b/versioned_docs/version-5.4/building-extensions/plugins/plugin-events/index.md index 526e053a..257618df 100644 --- a/versioned_docs/version-5.4/building-extensions/plugins/plugin-events/index.md +++ b/versioned_docs/version-5.4/building-extensions/plugins/plugin-events/index.md @@ -8,8 +8,6 @@ List of Plugin Events The list of events below includes the event name and a short description of its use, together with a link to the detailed description. -As described in [Joomla 4 and 5 changes](../joomla-4-and-5-changes.md), Joomla events have changed from being strings with associated parameters to "concrete" event classes specific to each event, sometimes via a "generic" event class. Some concrete event classes were introduced in Joomla 4 and others in Joomla 5. If you want your plugin to support both concrete and generic event classes then you need to code it as described in [Joomla 4 and 5 changes](../joomla-4-and-5-changes.md). - The event Group refers to the group of plugins which Joomla ensures are imported prior to dispatching that event. | Event Name | Short Description | Group | From Release | diff --git a/versioned_docs/version-5.4/building-extensions/plugins/plugin-events/installer.md b/versioned_docs/version-5.4/building-extensions/plugins/plugin-events/installer.md index 9fbdf84d..0764be91 100644 --- a/versioned_docs/version-5.4/building-extensions/plugins/plugin-events/installer.md +++ b/versioned_docs/version-5.4/building-extensions/plugins/plugin-events/installer.md @@ -13,9 +13,7 @@ Installer plugin events are triggered when some routines are performed during th For an overview of how a number of these events fit into the installation process see [Install Process](../../install-update/installation/install-process.md). (Note that the onExtensionBeforeInstall and onExtensionAfterInstall events aren't covered here). -For background on Joomla transitioning to using classes for events see [Joomla 4 and 5 changes](../joomla-4-and-5-changes.md), -where you can also find explanations for [accessing the arguments](../joomla-4-and-5-changes.md#summary---accessing-event-arguments) -and [returning values](../joomla-4-and-5-changes.md#summary---returning-values). +For background on obtaining arguments and returning results see [Plugin Methods and Arguments](../methods-and-arguments.md). ## onInstallerAddInstallationTab @@ -29,7 +27,11 @@ The event class `\Joomla\CMS\Event\Installer\AddInstallationTabEvent` has no arg ### Return Value -Return the contents of the additional tab which you want to include in the options for install. +Return the contents of the additional tab which you want to include in the options for install using, eg + +```php +$event->addResult($tabHTML); +``` ### Examples @@ -103,17 +105,7 @@ This event is triggered at the beginning of an installation operation. The event class `\Joomla\CMS\Event\Installer\BeforeInstallationEvent` has the following arguments: -- **`subject`** - This will be the FQN of the install model class Joomla\Component\Installer\Administrator\Model\InstallModel - -Note that there is no getter for the subject argument. Instead you should use: - -```php -public function onInstallerBeforeInstallation(\Joomla\CMS\Event\Installer\BeforeInstallationEvent $event): void -{ - $args = $event->getArguments(); - $subject = $args['subject']; -} -``` +- **`model`** - The instance of the install model class (\Joomla\Component\Installer\Administrator\Model\InstallModel) - **`package`** - This is null. @@ -138,17 +130,7 @@ but before Joomla has started to process the extension's installation files. The event class `\Joomla\CMS\Event\Installer\BeforeInstallerEvent` has the following arguments: -- **`subject`** - This will be the FQN of the install model class Joomla\Component\Installer\Administrator\Model\InstallModel - -Note that there is no getter for the subject argument. Instead you should use: - -```php -public function onInstallerBeforeInstaller(\Joomla\CMS\Event\Installer\BeforeInstallerEvent $event): void -{ - $args = $event->getArguments(); - $subject = $args['subject']; -} -``` +- **`model`** - The instance of the install model class (\Joomla\Component\Installer\Administrator\Model\InstallModel) - **`package`** - This is an array of 4 elements, which in the case of uploading a zip file will contain: @@ -188,21 +170,11 @@ This event is triggered after extension has been installed. The event class `\Joomla\CMS\Event\Installer\AfterInstallerEvent` has the following arguments: -- **`subject`** - This will be the FQN of the install model class Joomla\Component\Installer\Administrator\Model\InstallModel - -Note that there is no getter for the subject argument. Instead you should use: - -```php -public function onInstallerAfterInstaller(\Joomla\CMS\Event\Installer\AfterInstallerEvent $event): void -{ - $args = $event->getArguments(); - $subject = $args['subject']; -} -``` +- **`model`** - The instance of the install model class (\Joomla\Component\Installer\Administrator\Model\InstallModel) - **`package`** - The package which has been installed, in the form described in onInstallerBeforeInstaller above. -- **`installer`** - The installer class - Joomla sets this to "Joomla\CMS\Installer\Installer" +- **`installer`** - The instance of installer class (\Joomla\CMS\Installer\Installer) - **`installerResult`** - true or false, depending upon whether the package was successfully installed or not diff --git a/versioned_docs/version-5.4/building-extensions/plugins/plugin-examples/ajax-plugin.md b/versioned_docs/version-5.4/building-extensions/plugins/plugin-examples/ajax-plugin.md index 3ba3e77e..b54567ef 100644 --- a/versioned_docs/version-5.4/building-extensions/plugins/plugin-examples/ajax-plugin.md +++ b/versioned_docs/version-5.4/building-extensions/plugins/plugin-examples/ajax-plugin.md @@ -136,7 +136,7 @@ use My\Plugin\Ajax\AjaxJobs\Extension\Jobs; ``` ### Jobs file -This is where you write your code for your ad hoc jobs. The result is returned as described in [Joomla 4 and 5 Changes](../joomla-4-and-5-changes.md). +This is where you write your code for your ad hoc jobs. ```php title="plg_ajax_jobs/src/Extension/Jobs.php" "; } - if ($event instanceof ResultAwareInterface) { - $event->addResult($output); - } else { - $result = $event->getArgument('result') ?? []; - $result[] = $output; - $event->setArgument('result', $result); - } + $event->addResult($output); } } ``` diff --git a/versioned_docs/version-6.0/building-extensions/plugins/_assets/plg_shortcodes.zip b/versioned_docs/version-6.0/building-extensions/plugins/_assets/plg_shortcodes.zip deleted file mode 100644 index 1950497b9cdae9d6953bfe6fcf14e219e4d40e8f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3998 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 diff --git a/versioned_docs/version-6.0/building-extensions/plugins/_assets/shortcodes.jpg b/versioned_docs/version-6.0/building-extensions/plugins/_assets/shortcodes.jpg deleted file mode 100644 index ac7211d80781bc9453283983506abfdea1e1c360..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17135 zcmeIZWmIL&k~Vk_?hb`Z;qLAZg;Tf{?(SMx0foD}JB7Qu7H$VA+})jzxBJf5J#SC< zy)(aN)|xyk*3QVC>+Fmz85vJJ`M&hN0YLd8AuRy_0)YUjk00QD6AeM!-NF41n=bqxcYu5C{P9t7dmUjc{$pdcWjAR(ZjAfce4pM#%@BILxk8whN+_k@M+&>Bs z4E$rf&@ixY@E;u-Pyk>+aBwgPa7ai9h>zYrALjuOsE}wR%)(IUN(RuM9WYq@V{>6h zMQVC5m8UPrSPdNmVBxSnVdLPEQ&3V-)3C8~aB^|;h>D3zNJ>e6`KqF-rmmr>Wn^q( zYG!U>>E!I<>gMj@8Tcb8IOJz&SX_KUVp4KSYFb`?L19sGNoiSaU427iQ*%peZ(skw z;Lz~M=*;Zg{KDeWGH7#aduMlV|KRZG^6L8L_U``S@#!zSfFFRr(N1h`sf5n(vp%n^EzV=0*lXOXI70Y2*0-Hz*|)c|5fpY2cI?-GwFNn93>L=clEWzl=<1 zyxTgK%Vf}1%~Ul4VVph=YA22%3EV%k7v3Qwo15m$x>XWjv;1VV#OfAG~*&3h|brj_EO z2JEm7JWcCx8A{29A?&bGuM-Um{UK1kADljIEa;VPJj*G_*`0*P!|T|NFRyZNCp1EF zPiDOXie=UI@*z%1%tVRddz4X-C+ZHBq>J?ggKaBL3OUr{LY9ebRZ~ML`8G1LJO-V< z;w~oFDSht%Ppq_6N$-BLt_?LwJM~L7MNLx9DR1+GT2C)9%IdsLFL?MWTivc*De(r~ zTHk_ zLP1d@quckmAo~fY^Z=|X-`|2{(7nV~b4OL(e+tdRrT{(gKu)0~voQ|+P)T>SkIvw8 z5;q<Bb`iJI~bg7?dy1n*wV z)R-Wav|B#3e~yoo{AD|HeqDhTwE|nOphpyN$ojVAtX*`Ae@DemP)&doh>wmX5i>SU_0_NX0&B@sa= z*sfHgeiMzReH5q^hv$_LHU>n2d1c}gU4Ob$M zVC^lzk_$7q?mLj;yLwr@Q9g^vI-e$uq3@yEkg`SZ4tDQ9NN5f+zmU0nu3B*i^=cMN z20N=7qtsTNbJY%tL^!mu03+VmwzqW!Pp#m3T&F#(ujF?1N#G*uqYBRsbxn-t;bt=BCtr^P@O=U)UCO_xGA6j5#{ z=ZZfHY-obvbT;g9e0VM=F`z}@Pu=v;^wQ~R+!_=SOtxKO`xx;gxO$Fbtm>Og%@vzT z!UBYkXwkl*9bT?aSdhfX;K5wh@h)uxy)yQrb?}|p;U8)T=uXc368apG`c=kL*Gr(L zr_St|q=xqWaAu}8)39owY7pwQ;tWMnhKV=x)o~!~?YHU={x+xSI)cSXfv=aE6J3GL zb{&HRUm;ZP7pB&=WbB-9M-a-Crdv@w!vNYQSKBmFzxS+>T`8aDVQ-sB(%78ijhYo$ zBf#XLJbtqdIw&1@k{4x~(YZ!Dki?F}S(uo{4nGRfXIwx*SC>%O`7|RUWqgOG( zzKR>EIfeA#C1Mx1)ijXBmV!yDlA!^K%?K=EcLJoY>T*GxF9n1!?||b4&MXNV_`n0?O;zi5)FN@T6w`#=KwdysyAp1R=+GJzn_bzHvD$FF=d#Uw zV*e3qV0{LD^)~T!wq-GL2wQN#^+;2bYnun5bbHBKIJfncFf`s0z^R9Hp*FjlgAuZw z;%H+C;@I9VXhgrzehA0lw+Ej+6f|a6u0`xYnzbv_@RdYRSn{9{CD(TK1VDMVow7(% zv$ANYxdE^TJ8Yhkw5mNe*zM-jjeV*<7+M&}jO&tRYIsFN^4(EbSph$T70HYFwtTae ziareR`>kijdz16dBsmRM|4qag%?&-njqlZ?Xe8(Glq!x(3?gHg_;o48jLTl&V&}qX z?r9rzWV)xl% zZ!c#5V$T-`2wRxhm{v4ai^Da-qUI>t0|@y2h=gc7sdC_W{?Bu=Dn0m{ke1u4m*hJj zCoJntI9h%!Bj4`za@R46%?P_ z?W|AQIBepu5AL^O35tCOw107ZtmS`N@EO0?-pr&hvFWL4Po&L=k(8Gt9IP$X!sz5Q zC>i|*$Hoq`>p1#5Y#)2y{QStWv+91@-S+lo(6zJo(UcrP#ye*_fZq$(F7!oMAdDQ5c500!pa+Yf?#kom5OuX?u})>CiFHO{%t-jvuqE2tONtx` zyTtqM@V06+Oy3?CDR%1_XoEO$qhMuQ?T=w1WFjR+rBli`(nLwXLpYCqxK2BdYtgw( zUlVZWLH{Ei2o&$7fSPP|6JI3rlM?6*l>%TO?XzzppEji{CTqrt!lsD_aDxQ^l7wJ@ z!f;qfst|#S$Fq9nr?oR)?D;=K%GB~<5zi^_^Qrp$L&Z#Y%G(IaP=3a?Ju5=@~ z54z*;7YlxwtAQToeg|a5`5u_2-@z+=w-aywd>JEV2E&;M&Fi#?b1FT+-)C4<=y}O8 zEK+APLM~%;0adPZY&zCgcT~5sxe3AMRSk>SRK*&&SmXez`nD{ACXG>h0aWRRzPT^b z)zFHhKdHjpZHg!Hw<)+kVvfobH^EVLs*_2+q?v9wQ}o4J61{WZcfHz(<4dhHy?mR}6^}bxJ^BFwV`JjL02qCkmvR~f z1aq|uTQ4r7Ae3^Guu|Q&@3!{#JZ44m%<U`w}Ji1;iT$kp6FLrT_8adbHpxLVDppB4$4x zQoaRSY;9rbm(yVJ2O`X&R*qYaqEX&*EMrWQYb26}{E$ZOy#$WCq=Dksd@Ur2#wNzPk^a?j^4r6F3_Mrv zJ-L)G6e8-8Osa05zldpTayBD*Or}Wm`EhK!;dJK$r!|&SC`S9gQj*2-3p50lJkZ;n zo27YmhwZLo=lu){lR)4b#}X9Kz>hF4;hJPeEci~_*px(JRk|!z#I3Os_n6vx#eH>m zf(_2S=C5kEeOVDFPKR0@N3~+dbjl4dj$9qPZ+=c)ITylW_)aA30y(a(Zox8&59wt; z)>0RWkA5x4*M!r37*@j>7(R@>F2-zsZLID6hZQ{I?D;sd$f_# zrtCH*X_6qL-%uN>8ZkWA&DXO4M_tja!7Pa|MSc39&?I8lbKw!_s=?Uiv+}47f5!>| z;FTS58}3hw&l$4N38OfE0A9!#+xXoPqZk>^u?coQnERq5IptFs83#IlWr3W>T+OI6 zDKs!1BkcRCNr_-{XD{rkD_b;}1)aw2W-92cm-%r;XvR$<f^3zM)A#?AqIZT=eptm!!Q6<-hW7bj_)wM{5#;6`NxEds$P=k2Pap4K$Yi*p(Kv+ z(U~d-EJ8^f@S*(|@qJ(Ze;W!j4@CR_QJ8;2m;S$x!tnM`zEQ0~zqQkUyqOx6{1r3h zp?Ky9MNCi&hY^<<5k%wrd`6_b_OJQfo@@ffu^wsG)O!*_CgVcJ#)RUIu1SPkEv
        >8fL3O3$IJ>=|IsYw09VKi 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, ]` + +```php +public static function getSubscribedEvents(): array +{ + return [ + 'onContentPrepare' => ['myContentPrepareMethod', \Joomla\Event\Priority::HIGH], + 'onContentAfterTitle' => ['myContentAfterTitleMethod', \Joomla\Event\Priority::MIN], + ]; +} +``` + +See the other values available in libraries/vendor/joomla/event/src/Priority.php. +Use with caution though, as this will override any priority which the site administrator may want to set through ordering the plugins. + +## Stop Event Propagation + +To stop the propagation of an event to other plugins you can call + +```php +$event->stopPropagation(); +``` diff --git a/versioned_docs/version-6.0/building-extensions/plugins/basic-content-plugin.md b/versioned_docs/version-6.0/building-extensions/plugins/basic-content-plugin.md index b443ad34..243949b5 100644 --- a/versioned_docs/version-6.0/building-extensions/plugins/basic-content-plugin.md +++ b/versioned_docs/version-6.0/building-extensions/plugins/basic-content-plugin.md @@ -19,14 +19,25 @@ In addition, the plugin demonstrates the use of: - language constants - both in the manifest file and in the plugin code - returning a value from a plugin method - through the use of the `onContentAfterTitle` event. The plugin code adds some text after the article title. -You can test this plugin on both Joomla 4 and Joomla 5 instances, to see the differences in obtaining the parameters and returning the result (as described in [Joomla 4 and 5 changes](joomla-4-and-5-changes.md)). +The diagram below lists the plugin files to write, or you can download the plugin +from the Joomla Manual Examples repository [shortcodes plugin](https://github.com/joomla/manual-examples/tree/main/plugin-example-shortcode). -![Shortcodes plugin files](_assets/shortcodes.jpg "Shortcodes plugin files") - -The diagram shows the plugin files to write, or you can download a zip file of the plugin from [shortcodes plugin download](_assets/plg_shortcodes.zip). +``` +plg_shortcodes + ├─── language + │ └─── en-GB + │ ├─── plg_content_shortcodes.ini + │ └─── plg_content_shortcodes.sys.ini + ├─── services + │ └─── provider.php + ├─── src + │ └─── Extension + │ └─── Shortcode.php + └─── shortcodes.xml +``` ## Manifest File -For general information on manifest files see [Manifest Files](https://docs.joomla.org/Manifest_files). +For general information on manifest files see [Manifest Files](../install-update/installation/manifest.md). ```xml title="plg_shortcodes/shortcodes.xml" @@ -36,7 +47,7 @@ For general information on manifest files see [Manifest Files](https://docs.joom PLG_CONTENT_SHORTCODES_DESCRIPTION Me Today - (C) 2024 Open Source Matters, Inc. + (C) 2025 Open Source Matters, Inc. GNU General Public License version 2 or later My\Plugin\Content\Shortcodes @@ -58,7 +69,7 @@ Joomla is quite particular when it comes to plugin manifest files, and it's easy ``` -Previous sections described plugin types as 'content', 'system', etc but here the `type` is "plugin" (as it's the type of the extension) and the `group` refers to the plugin type. +Here the `type` is "plugin" (as it's the type of the extension) and the `group` refers to the plugin type. ### Language constants @@ -156,7 +167,7 @@ This is pretty much boilerplate code for obtaining your plugin from the Dependen use My\Plugin\Content\Shortcodes\Extension\Shortcode; ``` -Ensure that this aligns with your `` in the manifest file and your `namespace` statement and class name in the Extension class file. . +Ensure that this aligns with your `` in the manifest file and your `namespace` statement and class name in the Extension class file. ```php $config = (array) PluginHelper::getPlugin('content', 'shortcodes'); @@ -172,16 +183,7 @@ Ensure that this matches your class in your `src/Extension` directory. ### Extension Class This is the main code of the plugin. Hopefully the comments in the code explain what is going on. - -As explained in [Joomla 4 and 5 changes](./joomla-4-and-5-changes.md), code which triggers the Events can use a `GenericEvent` or a concrete Event, eg `ContentPrepareEvent`. In both these cases you can get the arguments using - -```php -[$context, $article, $params, $page] = array_values($event->getArguments()); -``` - -but you have to check in your code how to return the result. - -Using this approach means that you don't need to change your plugin code if the code which is triggering the event changes from using a generic Event class to a concrete event class. +For further details see the page on [Plugin Methods and Arguments](./methods-and-arguments.md). ```php title="plg_shortcodes/src/Extension/Shortcode.php" getApplication(); + + // The line below restricts the functionality to the front-end site (ie not admin/api/console job) // You may not want this, so you need to consider this in your own plugins - if (!$this->getApplication()->isClient('site')) { + if (!$app->isClient('site')) { return; } - - // use this format to get the arguments for both Joomla 4 and Joomla 5 - // In Joomla 4 a generic Event is passed - // In Joomla 5 a concrete ContentPrepareEvent is passed - [$context, $article, $params, $page] = array_values($event->getArguments()); + + // Find out if this event relates to an article or something else (eg contact, user) + $context = $event->getContext(); if ($context !== "com_content.article" && $context !== "com_content.featured") return; + // If we've reached here then it's an article - get the 'text' property + $article = $event->getItem(); $text = $article->text; // text of the article - $config = Factory::getApplication()->getConfig()->toArray(); // config params as an array + + $config = $app->getConfig()->toArray(); // config params as an array // (we can't do a foreach over the config params as a Registry because they're protected) // the following is just code to replace {configname} with the parameter value @@ -269,22 +274,22 @@ class Shortcode extends CMSPlugin implements SubscriberInterface $article->text = $text; } - public function addShortcodeSubtitle(Event $event) + public function addShortcodeSubtitle(AfterTitleEvent $event): void { - if (!$this->getApplication()->isClient('site')) { - return; - } - [$context, $article, $params, $page] = array_values($event->getArguments()); - if ($context !== "com_content.article" && $context !== "com_content.featured") return; - - $eventType = method_exists($event, 'getContext') ? "concrete event class" : "generic event class"; + /* This function adds a subtitle to a page on the site front end: + * "Processed for shortcodes" - if the page is an article + * "Not processed for shortcodes" - if the page is a contact, etc. + */ + + if (!$this->getApplication()->isClient('site')) return; - if ($event instanceof ResultAwareInterface) { - $event->addResult("{$eventType} via addResult"); + $context = $event->getContext(); + $this->loadLanguage(); + if ($context === "com_content.article" || $context === "com_content.featured") + { + $event->addResult(Text::_('PLG_CONTENT_SHORTCODES_PROCESSED')); } else { - $result = $event->getArgument('result') ?? []; - $result[] = "{$eventType} via setArgument"; - $event->setArgument('result', $result); + $event->addResult(Text::_('PLG_CONTENT_SHORTCODES_NOT_PROCESSED')); } } } @@ -302,6 +307,8 @@ Language constants which are used in the plugin code: ```php title="plg_shortcodes/language/en-GB/plg_content_shortcodes.ini" PLG_CONTENT_SHORTCODES_NO_MATCH="Error: no match for shortcode found" +PLG_CONTENT_SHORTCODES_PROCESSED="Processed for shortcodes" +PLG_CONTENT_SHORTCODES_NOT_PROCESSED="Not processed for shortcodes" ``` ## Installation @@ -313,4 +320,6 @@ On my Joomla instance {sitename} the default editor is {editor}. ``` Then navigate to a Joomla site menuitem which displays this article, either as a single article or as one of the featured articles. -The code should work on both Joomla 4 and Joomla 5, and should also display after the article title information about the type of event class used, and how the value is returned. +Include a non-existent configuration parameter in your shortcode to display the error text. + +Also compare the subtitle shown for a page displaying an article with a page displaying a contact. \ No newline at end of file diff --git a/versioned_docs/version-6.0/building-extensions/plugins/how-plugins-work.md b/versioned_docs/version-6.0/building-extensions/plugins/how-plugins-work.md index 8c076941..27c8f159 100644 --- a/versioned_docs/version-6.0/building-extensions/plugins/how-plugins-work.md +++ b/versioned_docs/version-6.0/building-extensions/plugins/how-plugins-work.md @@ -2,6 +2,12 @@ title: How Plugins Work sidebar_position: 1 --- + +How Plugins Work +================ + +## Overview + ![Plugins Overview](_assets/plugin-overview.jpg "Plugins Overview") The diagram shows how Joomla plugins work. @@ -20,6 +26,8 @@ The process of involving the plugins comprises 2 steps: 1. Import all plugins of a given type 2. Trigger an event +## Importing a Plugin + Importing a plugin type is implemented by the code: ```php PluginHelper::importPlugin($pluginType, …); @@ -36,12 +44,16 @@ Importing a plugin type involves: Splitting the plugins by plugin type in this way makes Joomla more performant - it doesn't need to process plugins which aren't going to be interested in the event which will be triggered next. +## Triggering an Event + The second step is triggering an event via a call to the event dispatcher. Joomla looks through its store of `Listeners` to determine which plugins have subscribed to that event type. It then calls the associated method of the subscribing plugin, and passes the event data to it, often allowing that data to be modified by the plugin code. Each plugin which has subscribed to that event is called in turn (based on a priority scheme), and any results returned by the plugins are collated into an array associated with the event. As an example, after Joomla is initialised system plugins are imported and the event `onAfterInitialise` is triggered. The "Remember Me" plugin (in plugins/system/remember) receives notification of this, and can log in a user who has previously checked the "Remember Me" checkbox on the login form. +## Subsequent Events + Once a plugin has been imported and its subscriptions logged in the `Listeners` data store, then it continues to receive notifications for all the events it has subscribed to. For example, consider the following scenario: - system plugins are imported, and the `onAfterInitialise` event is triggered - some time later content plugins are imported, and the `onContentPrepare` event is triggered. @@ -50,6 +62,8 @@ The system plugins imported in the first step can subscribe to the `onContentPre You should also be aware that unlike components and modules, there aren't different site plugins and administrator plugins. The plugins which you install are run for all the different contexts - site, administrator and API applications - so it's a good idea to in your plugins to check the application context. +## Sequence Diagram + For those who wish a more detailed picture of how plugins work, see the sequence diagram below. ```mermaid diff --git a/versioned_docs/version-6.0/building-extensions/plugins/joomla-4-and-5-changes.md b/versioned_docs/version-6.0/building-extensions/plugins/joomla-4-and-5-changes.md deleted file mode 100644 index 76af7ef9..00000000 --- a/versioned_docs/version-6.0/building-extensions/plugins/joomla-4-and-5-changes.md +++ /dev/null @@ -1,284 +0,0 @@ ---- -title: Joomla 4 and 5 Changes -sidebar_position: 2 ---- - -Joomla 4 and 5 Changes -====================== - -## Key changes -In Joomla 4 a number of significant changes began to be introduced to the plugins area: -1. Plugins were instantiated via the Dependency Injection Container, through the `services/provider.php` file. Previously plugins were run via the plugin's main php file. -2. Plugins subscribed to the events which they wanted to get notified about. Previously you wrote a method whose name matched the name of the event, and Joomla used PHP reflection classes to determine when your method got called. With the subscription mechanism Joomla checks if your plugin implements `Joomla\Event\SubscriberInterface` and if so calls `getSubscribedEvents` to which the plugin returns the event types it wants to handle, and the method which Joomla should call: -```php -public static function getSubscribedEvents(): array -{ - return [ - 'onContentPrepare' => 'myContentPrepareMethod', - 'onContentAfterTitle' => 'myContentAfterTitleMethod', - ]; -} -``` -3. Events were passed to plugins as Joomla Event objects. Previously each method associated with an event had parameters passed which were specific to that event type. For example, the method called when `onContentPrepare` was triggered was: -```php -public function onContentPrepare($context, &$row, $params, $page = 0) -``` -In Joomla 3 "events" basically comprised -- a string with the event name eg "onContentPrepare", and -- associated parameters, which were passed to the `onContentPrepare` method. - -The aim of the Joomla team has been to replace these with event classes, with each type of event having its own concrete event class. For example, for the `onContentPrepare` event there would be a `ContentPrepareEvent` class, with properties `$context`, `$row`, `$params` and `$page`. However, the process of replacing all the current code has been spread over a number of Joomla releases, some being done in Joomla 4 and some in Joomla 5. - -To enable plugins to use Event classes before the full concrete event class could be implemented a temporary solution was adopted, that is, to provide a `GenericEvent` class, which meant that plugins could be developed which used the Event parameter in their methods, rather than the list of parameters. - -**It's important to understand that the way you obtain the parameters of your plugin method, and the way which you provide your return value is different depending on whether a GenericEvent or a concrete class is used.** - -This is what we're now going to look at in detail. - -## Joomla 3 / 4 / 5 comparison -Let's take the example of `onContentPrepare` and consider how it has changed through these releases. - -### Joomla 3 -In Joomla 3 the event was triggered in `com_content` using -```php -$dispatcher->trigger('onContentPrepare', array ('com_content.article', &$item, &$item->params, $offset)); -``` -This resulted in a call to your plugin method: -```php -public function onContentPrepare($context, &$row, $params, $page = 0) -``` -and Joomla found this method by using the reflection class of your plugin class. - -`onContentPrepare` doesn't take any return value, but other plugins such as `onContentAfterTitle` do. To specify the return value you simply did -```php -return $value; -``` - -The Joomla team have provided backward compatibility for plugins through releases 4 and 5, so this mechanism will still work. However it will likely disappear in Joomla 6, so you should not write any new plugins this way. - -### Joomla 4 -In Joomla 4 the event is triggered using: -```php -$this->dispatchEvent(new Event('onContentPrepare', ['com_content.article', &$item, &$item->params, $offset])); -``` -This creates a `GenericEvent` which has Argument fields for the parameters above, which can be obtained via the `getArguments` method (using PHP array destructuring): -```php -[$context, $article, $params, $page] = array_values($event->getArguments()); -``` -The `array_values` is not really necessary here, but we'll see the reason for it shortly. - -For `GenericEvent` the `result` is an array which is held within the `GenericEvent` class, and you have to add any return value from your plugin into this array. So to return a value from a plugin method using `GenericEvent` you use: -```php -$result = $event->getArgument('result') ?: []; // get the result argument from GenericEvent -$result[] = $value; // add your return value into the array -$event->setArgument('result', $result); // write back the updated result into the GenericEvent instance -``` - -### Joomla 5 -In Joomla 5 the event is triggered using: -```php -$dispatcher->dispatch('onContentPrepare', new Content\ContentPrepareEvent('onContentPrepare', $contentEventArguments)); -``` -Here the concrete event class `ContentPrepareEvent` (from libraries/src/Event/Content/ContentPrepareEvent.php) is used. Such events may have specific getter methods (eg `getContext`), but you can still get the parameters using: -```php -[$context, $article, $params, $page] = array_values($event->getArguments()); -``` -Here the `array_values` is necessary, so if you use this to obtain the parameters it will work for both `GenericEvent` and concrete Event classes. - -To return a value your plugin should use `$event->addResult($value)`, but you can check if this method exists by -```php -use Joomla\CMS\Event\Result\ResultAwareInterface; -... - if ($event instanceof ResultAwareInterface) { - $event->addResult($value); - return; - } else { - // use GenericEvent approach - } -``` - -**If you're developing a developing a plugin to handle a `GenericEvent` then it's crucial to use the above mechanisms to avoid the plugin failing whenever the triggering code moves to using a concrete Event class.** - -## Summary - Accessing Event Arguments - -### Concrete Event Class - -You can use this approach if under libraries/src/Event there is a Joomla event class (known as a "concrete" event class) which is specific to the plugin event group. (Only a limited set is available for Joomla 4). - -```php -use Joomla\CMS\Plugin\CMSPlugin; -use Joomla\Event\SubscriberInterface; -use Joomla\CMS\Event\Content\ContentPrepareEvent; - -class MyPlugin extends CMSPlugin implements SubscriberInterface -{ - public static function getSubscribedEvents(): array - { - return [ - 'onContentPrepare' => 'myOnContentPrepare', - ]; - } - - public function myOnContentPrepare(ContentPrepareEvent $event) - { - $context = $event->getContext(); - $item = $event->getItem(); - $params = $event->getParams(); - $page = $event->getPage(); - // ... - } -``` - -Here in the getter call you must use the correct name for the argument, and in the Joomla manual documentation the correct name is always specified. - -You can also find the getter methods in the API documentation for the event class, for example [Event/Content/ContentPrepareEvent methods](cms-api://classes/Joomla-CMS-Event-Content-ContentPrepareEvent.html). - -To use an event class your plugin class must implement \Joomla\Event\SubscriberInterface and provide the `getSubscribedEvents` function. Your plugin listener function must then have just the single `$event` parameter. - -### Generic Event Class - -Use this approach after Joomla 4.0 if you wish to use event classes, but a concrete event class isn't available for your target Joomla version. - -```php -use Joomla\CMS\Plugin\CMSPlugin; -use Joomla\Event\SubscriberInterface; -use Joomla\Event\Event; - -class MyPlugin extends CMSPlugin implements SubscriberInterface -{ - public static function getSubscribedEvents(): array - { - return [ - 'onContentPrepare' => 'myOnContentPrepare', - ]; - } - - public function myOnContentPrepare(Event $event) - { - [$context, $item, $params, $page] = array_values($event->getArguments()); - ... - } -``` - -This approach also works for concrete event classes. - -It depends on the order of the arguments in the event class, and the correct order is always given in the documentation. - -There is a commitment from the Joomla team to preserve the same order within the event arguments as within the order of the parameters of the legacy method, but this will be removed in Joomla 6. This is implemented via the variable `$legacyArgumentsOrder`, eg in Joomla\CMS\Event\Content\ContentPrepareEvent (in libraries/src/Event/Content/ContentPrepareEvent.php): - -```php -class ContentPrepareEvent extends ContentEvent -{ - protected $legacyArgumentsOrder = ['context', 'subject', 'params', 'page']; -``` - -### ArrayAccess - -The Joomla Event class supports the [PHP ArrayAccess interface](https://www.php.net/manual/en/class.arrayaccess.php). -So you can use this method to obtain the arguments for both generic events and concrete events: - -```php -use Joomla\Event\Event; - - public function myOnContentPrepare(Event $event) - { - $context = $event['context']; - } -``` - -If the 'context' array element / argument doesn't exist then `$event['context']` returns null, -so you can use this to check if an argument exists. - -### Traditional Legacy Method - -You will be using this method if you developed your plugin before Joomla 4.0. In this case, you're probably better to continue this approach until concrete event classes are available for all the events your plugin listens for. - -You shouldn't use this approach for developing a new plugin, as you will have to rework it. - -To use the traditional (legacy) method, you specify a public function which has the same name as the event name, and Joomla uses PHP reflection to find your method. You specify the event parameters in your function signature, for example: - -```php -use Joomla\CMS\Plugin\CMSPlugin; - -class MyPlugin extends CMSPlugin -{ - public function onContentPrepare($context, $item, $params, $page) - { - if ($context == "com_content.article") ... - } -``` - -It doesn't matter what you specify as the names of your function parameters, just on their order in the parameter sequence. -The documentation in the Joomla manual always specifies the correct order of these arguments. - -## Summary - Returning Values - -### Concrete Event Class - -To return a value when using a concrete event class use `$event->addResult()`: - -```php -use Joomla\CMS\Plugin\CMSPlugin; -use Joomla\CMS\Event\Content\AfterTitleEvent; - -class MyPlugin extends CMSPlugin - - public function onContentAfterTitle(AfterTitleEvent $event) - { - ... - $event->addResult($value); - } -``` - -### Generic Event Class - -To return a value when using a generic event class use the following: - -```php -use Joomla\CMS\Plugin\CMSPlugin; -use Joomla\Event\Event; - -class MyPlugin extends CMSPlugin - - public function onContentAfterTitle(Event $event) - { - ... - $result = $event->getArgument('result') ?: []; // get the result argument from GenericEvent - $result[] = $value; // add your return value into the array - $event->setArgument('result', $result); - } -``` - -This approach will also work with concrete event classes. - -### Traditional Legacy Method - -To return a value when using the legacy method use the PHP `return`: - -``` - public function onContentAfterTitle($context, $item, $params, $page) - { - ... - return $value; - } -``` - -## Other New Features -### Priority -To use a plugin priority other than the default, specify this in your response to `getSubscribedEvents` -```php -public static function getSubscribedEvents(): array -{ - return [ - 'onContentPrepare' => ['myContentPrepareMethod', \Joomla\Event\Priority::HIGH], - 'onContentAfterTitle' => ['myContentAfterTitleMethod', \Joomla\Event\Priority::MIN], - ]; -} -``` -See the other values available in libraries/vendor/joomla/event/src/Priority.php. Use with caution though, as this will override any priority which the site administrator may want to set through ordering the plugins. - -### Stop Propagation -To stop the propagation of an event to other plugins you can call -```php -$event->stopPropagation(); -``` diff --git a/versioned_docs/version-6.0/building-extensions/plugins/methods-and-arguments.md b/versioned_docs/version-6.0/building-extensions/plugins/methods-and-arguments.md new file mode 100644 index 00000000..dcc8a259 --- /dev/null +++ b/versioned_docs/version-6.0/building-extensions/plugins/methods-and-arguments.md @@ -0,0 +1,307 @@ +--- +title: Plugin Methods and Arguments +sidebar_position: 2 +--- + +This section describes some general features of plugin code, +and many of these are used in the [plugin tutorial](./basic-content-plugin.md) which follows. + +Plugin Methods and Arguments +============================ + +There are 2 functions which you must implement within your plugin: + +1. The `getSubscribedEvents()` method. You tell Joomla + - which events you want to subscribe to + - the code to run when one of those events is triggered + +2. The handler function which Joomla should call when one of those events is triggered. + +This section describes these functions in detail. + +A note on terminology: you will find that the terms "dispatch", "trigger" and "emit" an event +are used interchangeably in both the documentation and in the Joomla code. + +## getSubscribedEvents method + +In this function you simply return an associative array where + +- the index is the event name +- the value is the name of the function in your plugin which handles that event + +```php +use Joomla\CMS\Plugin\CMSPlugin; +use Joomla\Event\SubscriberInterface; + +class MyPlugin extends CMSPlugin implements SubscriberInterface +{ + public static function getSubscribedEvents(): array + { + return [ + 'onContentPrepare' => 'modifyContent', + 'onContentAfterTitle' => 'addSubtitle', + ]; + } +``` + +It's important that you state that your plugin `implements SubscriberInterface`. + +(Note that you can also set a priority for each event and handler - see [Priority of Events](./advanced-features.md#priority-of-events)). + +## Your handler function + +Here is an example based on the [onContentPrepare event](./plugin-events/content.md#oncontentprepare): + +```php +use \Joomla\CMS\Event\Content\ContentPrepareEvent; + +public function modifyContent(ContentPrepareEvent $event): void +{ + if (!$this->getApplication()->isClient('site')) { + return; + } + $item = $event->getItem(); // preferred way to get the event arguments + $args = $event->getArguments(); // fallback mechanism + $context = $event['context']; // using PHP Array Access + if ($context) { + ... + } +} +``` + +The subsections below provide some detailed descriptions. + +### Event Class + +Each event has its own PHP class, and you can find these in libraries/src/Event. +Some event classes are in this directory, but most are in subdirectories categorised by the area of Joomla where that event is triggered. + +The name of the event is often similar to the class name, +eg the event class \Joomla\CMS\Event\Content\ContentPrepareEvent is associated with the event onContentPrepare. +This is an event which is of type "content", in the sense that plugins of type "content" are imported prior to it being triggered. +However, there isn't a "type" field associated with the event instance which defines this plugin type (`$event` doesn't have a "type" property). + +There is a mapping between many event names and event classes in libraries/src/Event/CoreEventAware.php. + +You may be wondering: "How do I find out which event to use to achieve what I want?" + +If a suitable event isn't documented in this manual, and a response from AI doesn't give you a useful answer, +then there are a few options to consider: + +- look through the event classes below libraries/src/Event or in libraries/vendor/composer/autoload_classmap.php +- check for the mapping to event name in libraries/src/Event/CoreEventAware.php +- check the outdated [Plugin Events documentation](https://docs.joomla.org/Plugin/Events) +- search the Joomla code for the possible event names and event class names. + +### Checking the Client + +It's important to remember that plugins are run whenever Joomla is run: + +- the front-end site +- the back-end administrator +- using the Joomla API +- running a console job + +So in your event handler code you should generally check the context using something like: + +```php +if (!$this->getApplication()->isClient('site')) { + return; +} +``` + +The [getApplication](#getapplication) function is an inherited method, as described below. + +### Getter Functions + +Each Event instance has a number of arguments which are documented in the detailed plugin event pages +(and sometimes in the source code of the event class). + +You should use the event getter functions to obtain the event arguments, for example + +```php +$context = $event->getContext(); +$item = $event->getItem(); +$params = $event->getParams(); +$page = $event->getPage(); +``` + +The getter function matches the name of the argument in the documentation. + +`$event->getName()` returns the name of the event. + +### Arguments + +Occasionally you may find that there isn't a getter function for a particular argument. +In this case you can obtain the event arguments by: + +```php +$args = $event->getArguments(); +``` + +The returned value is an associative array mapping the arguments to their values, eg: + +```php +["context" => "com_content.article", "subject" => , + "params" =>
        , "page" => 0] +``` + +(Note that the array element index doesn't always match the argument in the documentation pages. +For example, for ContentPrepareEvent the 'item' argument is given by the "subject" element in the array.) + +### Array Access + +The Joomla Event class supports the [PHP ArrayAccess interface](https://www.php.net/manual/en/class.arrayaccess.php). +So you can use this method to obtain the arguments, and you may find this useful in the case where an explicit getter function is not available. + +```php +use \Joomla\CMS\Event\Content\ContentPrepareEvent; + + public function modifyContent(ContentPrepareEvent $event): void + { + $context = $event['context']; + if ($context) { + ... + } + } +``` + +If the 'context' array element / argument doesn't exist then `$event['context']` returns null, +so you can use this to check if an argument exists. No PHP warning is generated if the array index is not present. + +### Updating arguments and returning results + +The documentation will state whether any arguments are modifiable, or whether a result should be returned. + +For example, ContentPrepareEvent passes a 'subject' argument (available via `getItem()`) which you can modify. +Depending upon the 'context' argument this item may be an article, a contact, a user, etc., +and you can set properties of this object. + +```php +$context = $event->getContext(); +if ($context === "com_content.article") { + $article = $event->getItem(); + $article->text = $article->text . " [end of text]"; // Appends " [end of text]" to the end of the article +} +``` + +(In this case the appended text would be included in the HTTP response, but not saved to the database). + +If the event allows a result to be returned then you should use this approach: + +```php +use Joomla\CMS\Event\Content\AfterTitleEvent; + + public function onContentAfterTitle(AfterTitleEvent $event) + { + ... + $event->addResult($value); + } +``` + +Sometimes the event provides a specific function which allows you to update an entity. +For example, the [onInstallerBeforeInstaller](./plugin-events/installer.md#oninstallerbeforeinstaller) event +allows the plugin to change the package which is being installed: + +```php +public function onInstallerBeforeInstaller(\Joomla\CMS\Event\Installer\BeforeInstallerEvent $event): void +{ + $newpkg = array("dir" => $pathToExtensionDirectory, "type" => "plugin", "extractdir" => null, "packagefile" => null); + $event->updatePackage($newpkg); +} +``` + +## Methods available to Plugins + +Inside your plugin you have access to several methods. + +### getApplication + +- `$this->getApplication()` returns the Application instance. (This method is inherited from CMSPlugin). +However, you need to inject this dependency in your plugin's services/provider.php file. + +```php + $plugin = new MyPlugin($subject, $config); + $app = Joomla\CMS\Factory::getApplication(); + $plugin->setApplication($app); +``` + +From the `$app` instance you can find other details +such as the global configuration (getConfig), current user (getIdentity), language (getLanguage), document (getDocument), etc. + +### loadLanguage + +- `$this->loadLanguage()` (also inherited) will load your plugin's language constants. +You have to load them before using them in your code. + +Alternatively you can set within your plugin class: + +```php +protected $autoloadLanguage = true; +``` + +and Joomla will load your plugin's language constants for you. + +:::warning + This feature has a negative effect on performance, as it means that your language constants are processed even when they're not used. + You should always load them manually. +::: + +### Injected Dependencies + +If your plugin needs access to other Joomla class instances which are in the main Dependency Injection container then you should follow this pattern: + +- in your services/provider.php file get the instance out of the DI container and pass it into your plugin's setter method + +- in your plugin use your getter method to retrieve it. + +- include these getter and setter methods in your plugin by `use` + the appropriate trait + +For example, to include the database object do this in your services/provider.php file: + +```php + $plugin = new MyPlugin( (array) PluginHelper::getPlugin('content', 'myplugin') ); + $plugin->setDatabase($container->get(DatabaseInterface::class)); +``` + +and include this in your plugin file: + +```php +use Joomla\CMS\Plugin\CMSPlugin; +use Joomla\Event\SubscriberInterface; +use Joomla\Database\DatabaseAwareTrait; + +class MyPlugin extends CMSPlugin implements SubscriberInterface +{ + use DatabaseAwareTrait; + + public function modifyContent(ContentPrepareEvent $event): void + { + $db = $this->getDatabase(); + } +} +``` + +As another example, if your plugin sends mail then you can set the MailerFactory as a dependency. + +```php title="services/provider.php" + $plugin = new MyPlugin( (array) PluginHelper::getPlugin('content', 'myplugin') ); + $plugin->setMailerFactory($container->get(MailerFactoryInterface::class)); +``` + +```php title="plugin file" +use Joomla\CMS\Mail\MailerFactoryAwareTrait; + +class MyPlugin extends CMSPlugin implements SubscriberInterface +{ + use MailerFactoryAwareTrait; + + public function modifyContent(ContentPrepareEvent $event): void + { + $mailer = $this->getMailerFactory()->createMailer(); + } +} +``` + +These traits are scattered throughout the Joomla libraries directories, +but you can find them by searching for filenames which match `*AwareTrait.php`. \ No newline at end of file diff --git a/versioned_docs/version-6.0/building-extensions/plugins/plugin-events/application.md b/versioned_docs/version-6.0/building-extensions/plugins/plugin-events/application.md index 9e96aae2..2c9c5249 100644 --- a/versioned_docs/version-6.0/building-extensions/plugins/plugin-events/application.md +++ b/versioned_docs/version-6.0/building-extensions/plugins/plugin-events/application.md @@ -54,7 +54,7 @@ If you would like further information then you may find it helpful to watch the ## Application Events overview The application event classes can be found in libraries/src/Event/Application. -In general they have a single parameter `subject` which is set to the Application instance, +In general they have a single parameter `application` which is set to the Application instance, and the getter method for this parameter is `getApplication`, for example: ```php @@ -133,7 +133,7 @@ public function onAfterInitialise(AfterInitialiseEvent $event): void The event class \Joomla\CMS\Event\Application\AfterInitialiseEvent has the following arguments: -- **`subject`** - the Application instance, available via `$event->getApplication()` +- **`application`** - the Application instance, available via `$event->getApplication()` ### Return Value @@ -151,7 +151,7 @@ You can use this event, for example, to tidy up aspects after the routing functi The event class \Joomla\CMS\Event\Application\AfterRouteEvent has the following arguments: -- **`subject`** - the Application instance, available via `$event->getApplication()` +- **`application`** - the Application instance, available via `$event->getApplication()` ### Return Value @@ -169,9 +169,9 @@ You can use this event, to set aspects of the Document. The event class \Joomla\CMS\Event\Application\AfterInitialiseDocumentEvent has the following arguments: -- **`subject`** - the Application instance, available via `$event->getApplication()` +- **`application`** - the Application instance, available via `$event->getApplication()` -- **`document`** - The Document. You can use the getter method `$doc = $event->getDocument();` to get this. +- **`document`** - The Document, available via `$event->getDocument()` ### Return Value @@ -187,7 +187,7 @@ This event is triggered after Joomla has run the main component and buffered its The event class \Joomla\CMS\Event\Application\AfterDispatchEvent has the following arguments: -- **`subject`** - the Application instance, available via `$event->getApplication()` +- **`application`** - the Application instance, available via `$event->getApplication()` ### Return Value @@ -204,7 +204,7 @@ to fill in the `` tags. The event class \Joomla\CMS\Event\Application\BeforeRenderEvent has the following arguments: -- **`subject`** - the Application instance, available via `$event->getApplication()` +- **`application`** - the Application instance, available via `$event->getApplication()` ### Return Value @@ -222,9 +222,9 @@ You can use this to include other items within the ``, via the [Web Asset The event class \Joomla\CMS\Event\Application\BeforeCompileHeadEvent has the following arguments: -- **`subject`** - the Application instance, available via `$event->getApplication()` +- **`application`** - the Application instance, available via `$event->getApplication()` -- **`document`** - The Document. You can use the getter method `$doc = $event->getDocument();` to get this. +- **`document`** - The Document, available via `$event->getDocument()` ### Return Value @@ -252,7 +252,7 @@ public function onAfterRender(AfterRenderEvent $event): void The event class \Joomla\CMS\Event\Application\AfterRenderEvent has the following arguments: -- **`subject`** - the Application instance, available via `$event->getApplication()` +- **`application`** - the Application instance, available via `$event->getApplication()` ### Return Value @@ -268,7 +268,7 @@ This event is triggered just before Joomla sends the HTTP response. The event class \Joomla\CMS\Event\Application\BeforeRespondEvent has the following arguments: -- **`subject`** - the Application instance, available via `$event->getApplication()` +- **`application`** - the Application instance, available via `$event->getApplication()` ### Return Value @@ -284,7 +284,7 @@ This event is triggered just after Joomla sends the HTTP response. The event class \Joomla\CMS\Event\Application\AfterRespondEvent has the following arguments: -- **`subject`** - the Application instance, available via `$event->getApplication()` +- **`application`** - the Application instance, available via `$event->getApplication()` ### Return Value @@ -304,7 +304,7 @@ which could be used by the extension's `boot()` method (which is run when the ex The event class \Joomla\CMS\Event\BeforeExtensionBootEvent has the following arguments: -- **`subject`** - the Application instance, but note that there is no getter function for this. Instead you can use: +- **`application`** - the Application instance, but note that there is no getter function for this. Instead you can use: ```php use \Joomla\CMS\Event\BeforeExtensionBootEvent; @@ -315,14 +315,9 @@ public function onBeforeExtensionBoot(BeforeExtensionBootEvent $event): void } ``` -- **`type`** - This is the type of extension being booted, +- **`extensionType`** - This is the type of extension being booted, identified by the interface it supports, eg "Joomla\CMS\Extension\PluginInterface". - -Note that the getter function for this argument is getExtensionType rather than getType: - -```php -$type = $event->getExtensionType(); -``` +Available via `$event->getExtensionType()` - **`extensionName`** - The extension name, available via `$event->getExtensionName()`: @@ -361,7 +356,7 @@ then com_content will run your MVC classes instead of its own. The event class \Joomla\CMS\Event\AfterExtensionBootEvent has the following arguments: -- **`subject`** - the Application instance, but note that there is no getter function for this. Instead you can use: +- **`application`** - the Application instance, but note that there is no getter function for this. Instead you can use: ```php use \Joomla\CMS\Event\AfterExtensionBootEvent; @@ -372,10 +367,9 @@ public function onBeforeExtensionBoot(AfterExtensionBootEvent $event): void } ``` -- **`type`** - This is the type of extension booted, +- **`extensionType`** - This is the type of extension being booted, identified by the interface it supports, eg "Joomla\CMS\Extension\PluginInterface". - -Note that the getter function for this argument is getExtensionType rather than getType: +Available via `$event->getExtensionType()` ```php $type = $event->getExtensionType(); diff --git a/versioned_docs/version-6.0/building-extensions/plugins/plugin-events/content.md b/versioned_docs/version-6.0/building-extensions/plugins/plugin-events/content.md index a37c1f55..3b04e6a4 100644 --- a/versioned_docs/version-6.0/building-extensions/plugins/plugin-events/content.md +++ b/versioned_docs/version-6.0/building-extensions/plugins/plugin-events/content.md @@ -15,7 +15,7 @@ They are triggered during the process of: The sections below give a brief description of each content event, what the event parameters / arguments are, and any examples of their use which are available in the Joomla Manual. -For background on Joomla transitioning to using classes for events see [Joomla 4 and 5 changes](../joomla-4-and-5-changes.md), where you can also find explanations for [accessing the arguments](../joomla-4-and-5-changes.md#summary---accessing-event-arguments) and [returning values](../joomla-4-and-5-changes.md#summary---returning-values). +For background on obtaining arguments and returning results see [Plugin Methods and Arguments](../methods-and-arguments.md). ## onContentPrepare diff --git a/versioned_docs/version-6.0/building-extensions/plugins/plugin-events/index.md b/versioned_docs/version-6.0/building-extensions/plugins/plugin-events/index.md index 526e053a..257618df 100644 --- a/versioned_docs/version-6.0/building-extensions/plugins/plugin-events/index.md +++ b/versioned_docs/version-6.0/building-extensions/plugins/plugin-events/index.md @@ -8,8 +8,6 @@ List of Plugin Events The list of events below includes the event name and a short description of its use, together with a link to the detailed description. -As described in [Joomla 4 and 5 changes](../joomla-4-and-5-changes.md), Joomla events have changed from being strings with associated parameters to "concrete" event classes specific to each event, sometimes via a "generic" event class. Some concrete event classes were introduced in Joomla 4 and others in Joomla 5. If you want your plugin to support both concrete and generic event classes then you need to code it as described in [Joomla 4 and 5 changes](../joomla-4-and-5-changes.md). - The event Group refers to the group of plugins which Joomla ensures are imported prior to dispatching that event. | Event Name | Short Description | Group | From Release | diff --git a/versioned_docs/version-6.0/building-extensions/plugins/plugin-events/installer.md b/versioned_docs/version-6.0/building-extensions/plugins/plugin-events/installer.md index 9fbdf84d..0764be91 100644 --- a/versioned_docs/version-6.0/building-extensions/plugins/plugin-events/installer.md +++ b/versioned_docs/version-6.0/building-extensions/plugins/plugin-events/installer.md @@ -13,9 +13,7 @@ Installer plugin events are triggered when some routines are performed during th For an overview of how a number of these events fit into the installation process see [Install Process](../../install-update/installation/install-process.md). (Note that the onExtensionBeforeInstall and onExtensionAfterInstall events aren't covered here). -For background on Joomla transitioning to using classes for events see [Joomla 4 and 5 changes](../joomla-4-and-5-changes.md), -where you can also find explanations for [accessing the arguments](../joomla-4-and-5-changes.md#summary---accessing-event-arguments) -and [returning values](../joomla-4-and-5-changes.md#summary---returning-values). +For background on obtaining arguments and returning results see [Plugin Methods and Arguments](../methods-and-arguments.md). ## onInstallerAddInstallationTab @@ -29,7 +27,11 @@ The event class `\Joomla\CMS\Event\Installer\AddInstallationTabEvent` has no arg ### Return Value -Return the contents of the additional tab which you want to include in the options for install. +Return the contents of the additional tab which you want to include in the options for install using, eg + +```php +$event->addResult($tabHTML); +``` ### Examples @@ -103,17 +105,7 @@ This event is triggered at the beginning of an installation operation. The event class `\Joomla\CMS\Event\Installer\BeforeInstallationEvent` has the following arguments: -- **`subject`** - This will be the FQN of the install model class Joomla\Component\Installer\Administrator\Model\InstallModel - -Note that there is no getter for the subject argument. Instead you should use: - -```php -public function onInstallerBeforeInstallation(\Joomla\CMS\Event\Installer\BeforeInstallationEvent $event): void -{ - $args = $event->getArguments(); - $subject = $args['subject']; -} -``` +- **`model`** - The instance of the install model class (\Joomla\Component\Installer\Administrator\Model\InstallModel) - **`package`** - This is null. @@ -138,17 +130,7 @@ but before Joomla has started to process the extension's installation files. The event class `\Joomla\CMS\Event\Installer\BeforeInstallerEvent` has the following arguments: -- **`subject`** - This will be the FQN of the install model class Joomla\Component\Installer\Administrator\Model\InstallModel - -Note that there is no getter for the subject argument. Instead you should use: - -```php -public function onInstallerBeforeInstaller(\Joomla\CMS\Event\Installer\BeforeInstallerEvent $event): void -{ - $args = $event->getArguments(); - $subject = $args['subject']; -} -``` +- **`model`** - The instance of the install model class (\Joomla\Component\Installer\Administrator\Model\InstallModel) - **`package`** - This is an array of 4 elements, which in the case of uploading a zip file will contain: @@ -188,21 +170,11 @@ This event is triggered after extension has been installed. The event class `\Joomla\CMS\Event\Installer\AfterInstallerEvent` has the following arguments: -- **`subject`** - This will be the FQN of the install model class Joomla\Component\Installer\Administrator\Model\InstallModel - -Note that there is no getter for the subject argument. Instead you should use: - -```php -public function onInstallerAfterInstaller(\Joomla\CMS\Event\Installer\AfterInstallerEvent $event): void -{ - $args = $event->getArguments(); - $subject = $args['subject']; -} -``` +- **`model`** - The instance of the install model class (\Joomla\Component\Installer\Administrator\Model\InstallModel) - **`package`** - The package which has been installed, in the form described in onInstallerBeforeInstaller above. -- **`installer`** - The installer class - Joomla sets this to "Joomla\CMS\Installer\Installer" +- **`installer`** - The instance of installer class (\Joomla\CMS\Installer\Installer) - **`installerResult`** - true or false, depending upon whether the package was successfully installed or not diff --git a/versioned_docs/version-6.0/building-extensions/plugins/plugin-examples/ajax-plugin.md b/versioned_docs/version-6.0/building-extensions/plugins/plugin-examples/ajax-plugin.md index 0c4894f4..b54567ef 100644 --- a/versioned_docs/version-6.0/building-extensions/plugins/plugin-examples/ajax-plugin.md +++ b/versioned_docs/version-6.0/building-extensions/plugins/plugin-examples/ajax-plugin.md @@ -136,7 +136,7 @@ use My\Plugin\Ajax\AjaxJobs\Extension\Jobs; ``` ### Jobs file -This is where you write your code for your ad hoc jobs. The result is returned as described in [Joomla 4 and 5 Changes](../joomla-4-and-5-changes.md). +This is where you write your code for your ad hoc jobs. ```php title="plg_ajax_jobs/src/Extension/Jobs.php" get(DatabaseInterface::class); - $query = $db->getQuery(true) + $query = $db->createQuery() ->select('type, count(*) as count') ->from($db->quoteName('#__extensions')) ->group('type'); @@ -177,13 +177,7 @@ class Jobs extends CMSPlugin implements SubscriberInterface $output .= "{$extension}:{$count}
        "; } - if ($event instanceof ResultAwareInterface) { - $event->addResult($output); - } else { - $result = $event->getArgument('result') ?? []; - $result[] = $output; - $event->setArgument('result', $result); - } + $event->addResult($output); } } ``` From 7cefd14edae4e3d74434a83cb71e9c1402cdd69b Mon Sep 17 00:00:00 2001 From: Robbie Jackson Date: Wed, 10 Dec 2025 21:02:20 +0000 Subject: [PATCH 5/5] minor improvements --- docs/building-extensions/plugins/methods-and-arguments.md | 4 ++-- .../building-extensions/plugins/methods-and-arguments.md | 4 ++-- .../building-extensions/plugins/methods-and-arguments.md | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/building-extensions/plugins/methods-and-arguments.md b/docs/building-extensions/plugins/methods-and-arguments.md index dcc8a259..299619aa 100644 --- a/docs/building-extensions/plugins/methods-and-arguments.md +++ b/docs/building-extensions/plugins/methods-and-arguments.md @@ -173,7 +173,7 @@ so you can use this to check if an argument exists. No PHP warning is generated The documentation will state whether any arguments are modifiable, or whether a result should be returned. -For example, ContentPrepareEvent passes a 'subject' argument (available via `getItem()`) which you can modify. +For example, ContentPrepareEvent passes an 'item' argument which you can modify. Depending upon the 'context' argument this item may be an article, a contact, a user, etc., and you can set properties of this object. @@ -244,7 +244,7 @@ and Joomla will load your plugin's language constants for you. :::warning This feature has a negative effect on performance, as it means that your language constants are processed even when they're not used. - You should always load them manually. + You should always load them manually using `$this->loadLanguage()` instead. ::: ### Injected Dependencies diff --git a/versioned_docs/version-5.4/building-extensions/plugins/methods-and-arguments.md b/versioned_docs/version-5.4/building-extensions/plugins/methods-and-arguments.md index dcc8a259..299619aa 100644 --- a/versioned_docs/version-5.4/building-extensions/plugins/methods-and-arguments.md +++ b/versioned_docs/version-5.4/building-extensions/plugins/methods-and-arguments.md @@ -173,7 +173,7 @@ so you can use this to check if an argument exists. No PHP warning is generated The documentation will state whether any arguments are modifiable, or whether a result should be returned. -For example, ContentPrepareEvent passes a 'subject' argument (available via `getItem()`) which you can modify. +For example, ContentPrepareEvent passes an 'item' argument which you can modify. Depending upon the 'context' argument this item may be an article, a contact, a user, etc., and you can set properties of this object. @@ -244,7 +244,7 @@ and Joomla will load your plugin's language constants for you. :::warning This feature has a negative effect on performance, as it means that your language constants are processed even when they're not used. - You should always load them manually. + You should always load them manually using `$this->loadLanguage()` instead. ::: ### Injected Dependencies diff --git a/versioned_docs/version-6.0/building-extensions/plugins/methods-and-arguments.md b/versioned_docs/version-6.0/building-extensions/plugins/methods-and-arguments.md index dcc8a259..299619aa 100644 --- a/versioned_docs/version-6.0/building-extensions/plugins/methods-and-arguments.md +++ b/versioned_docs/version-6.0/building-extensions/plugins/methods-and-arguments.md @@ -173,7 +173,7 @@ so you can use this to check if an argument exists. No PHP warning is generated The documentation will state whether any arguments are modifiable, or whether a result should be returned. -For example, ContentPrepareEvent passes a 'subject' argument (available via `getItem()`) which you can modify. +For example, ContentPrepareEvent passes an 'item' argument which you can modify. Depending upon the 'context' argument this item may be an article, a contact, a user, etc., and you can set properties of this object. @@ -244,7 +244,7 @@ and Joomla will load your plugin's language constants for you. :::warning This feature has a negative effect on performance, as it means that your language constants are processed even when they're not used. - You should always load them manually. + You should always load them manually using `$this->loadLanguage()` instead. ::: ### Injected Dependencies