From d87ca8929c747eea85baca8d0129d4777371a921 Mon Sep 17 00:00:00 2001 From: "Nicholas K. Dionysopoulos" Date: Fri, 18 Aug 2023 19:49:56 +0300 Subject: [PATCH] Add more information about plugins --- sections/modules.xml | 7 +- sections/plugins.xml | 222 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 224 insertions(+), 5 deletions(-) diff --git a/sections/modules.xml b/sections/modules.xml index 6ec0f26..9acf439 100644 --- a/sections/modules.xml +++ b/sections/modules.xml @@ -751,9 +751,10 @@ class ExampleHelper implements DatabaseAwareInterface Modules and com_ajax Sometimes, you need to have your modules return data asynchronously - to the HTML page rendering, using client-side (JavaScript) code. The best - way to do that is by using Joomla's com_ajax - component. + to the HTML page rendering, using client-side (JavaScript) code. DO NOT + use arbitrarily named .php files which will be accessed directly over the + web; this is a terrible and insecure practice. The best way to do that is + by using Joomla's com_ajax component. com_ajax is a special core component built into Joomla. By itself, it can't do much. Its job is to let non-component, first-class Joomla diff --git a/sections/plugins.xml b/sections/plugins.xml index 9406d91..b4f7bdc 100644 --- a/sections/plugins.xml +++ b/sections/plugins.xml @@ -1190,13 +1190,231 @@ implements \Joomla\Database\DatabaseAwareInterface
View Templates - + I know what you're thinking: “what's the fox doing in the country + fair?”, er, ”what are view templates doing in a plugin?”. And yet, they + do make sense in some use cases. Core Joomla! uses + them for the voting, page navigation, and page break content plugins, for + the WebAuthn Multi-factor Authentication plugin (which needs to render its + own, different interface to the regular code entry interface of most MFA + plugins), as well as all custom fields plugins. + + And now you probably went “a-ha!”. Yes, plugins need to have view + templates when they are supposed to emit precomposed HTML, usually to be + injected into some content. + + Back in the olden days (Joomla! 1.x and 2.x) there was no such + thing. HTML generation was taking place in the code which, as I hope you + understand by reading this book, is a Very Bad Thing. As to why it is such + a bad thing, ask any site integrator who cut their teeth on Joomla! 1.x + and 2.5. They will tell you that trying to coax that output to display the + way they needed to was a royal pain in the posterior, if at all possible. + Having view templates in plugins means they can be overridden, ergo you + have separated the business logic of your plugin from the presentation of + its results, making both the developer (that's you!) and the site + integrator happy. + + View templates for plugins are always stored in the + tmpl folder of the plugin. + + Using a view template is a two step process. First, you need to get + the path to the view template file (remember, it might be overridden!) + using Joomla's + \Joomla\CMS\Plugin\PluginHelper::getLayoutPath + method: + + $path = \Joomla\CMS\Plugin\PluginHelper::getLayoutPath( + 'system', 'example', 'foobar' +); + + The three arguments are the plugin folder, the plugin name, and the + view template name. In this case, we are looking for the + foobar.php view template file inside + plugins/system/example/tmpl (or its override in the + current template). + + The second step is include this file and capturing its + output: + + ob_start(); +include $path; +$html = ob_get_clean(); + + + If you are writing a custom field plugin you will NOT be directly + accessing the view template file. This is done for you by the + \Joomla\Component\Fields\Administrator\Plugin\FieldsPlugin + core class your plugin extends from. +
Plugins and com_ajax - + Sometimes, you need to have your plugins respond to asynchronous + requests using client-side (JavaScript) code, or you need to provide a + “callback URL” (e.g. when implementing an OAuth2 authentication scheme, + interfacing with a payments processor service, etc). DO NOT use + arbitrarily named .php files which will be accessed directly over the web; + this is a terrible and insecure practice. The best way to do that is by + using Joomla's com_ajax component. + + com_ajax is a special core component built into Joomla. By itself, + it can't do much. Its job is to let non-component, first-class Joomla + extension types (modules, plugins, and templates) handle requests and + return results either as JSON or as any arbitrary format. + + For plugins, you would need to load a URL like this: + index.php?option=com_ajax&group=system&plugin=example&method=something&format=raw. + If you are working in the backend you obviously need to use + administrator/index.php. + + As you can see, the plugin you will be using is specified by the + group and plugin URL parameters. + + The way this works is that com_ajax will make sure your plugin is + loaded, then fire the plugin event + onAjaxMethodname, where + Methodname is the value of the + method URL parameter. In the example above, it would fire the + plugin event onAjaxSomething. The event must return a result. + + + If you use format=raw, one of the following will happen: + + + + If you throw an exception or return a Throwable, it will set the + HTTP status to the throwable's status code and return a body similar + to "RuntimeException:Your throwable's message". + + + + If you return a scalar (string, null, integer, float) it will + return its string representation. + + + + If you return an object or array it will try to first cast it as + an array, then implode() it into a string. This + is quite useless, so please don't do that. + + + + If you use format=json it will try to convert your data to a JSON + representation using Joomla's + Joomla\CMS\Response\JsonResponse class. The + returned JSON object has the following keys: + + + + success + + + Boolean true if your method has neither thrown an exception, + nor returned a Throwable. Boolean false otherwise. + + + + + message + + + Only when success is false. If your method threw an exception, + or returned a Throwable object: the message of that throwable. + Otherwise, this key is not set. + + + + + messages + + + Any enqueued Joomla! application messages. The messages are + categorised by type, therefore they may be returned out of order. + For example: + + messages: { + "warning": [ + "Some warning", + "Another warning" + ], + "error": [ + "The code went belly up. Whoopsie!" + ] +} + + + + + data + + + Only when success is true. The data returned by your + method. + + + + + Practical limitations + + The com_ajax component will not pass any data to the plugin event. + If you need the request data you must go through the Joomla application's + input object. + + The event name is not guaranteed to be unique to your plugin, since + the naming scheme is very simplistic: + onAjaxMethodname. It is strongly + recommended to use two precautions to avoid mishaps with third party + plugins: + + + + Use a method which is highly specific to your plugin e.g. have + your URL method parameter set to something like + acmePaymentsPayPalCallback instead of just + callback. The former will fire the event named + onAjaxAcmePaymentsPayPalCallback which is reasonably + guaranteed to be unique across plugins installed on the site, the + latter will fire the event named onAjaxCallback which is + reasonably NOT guaranteed to be unique. This protects your plugin from + getting interfered with by other people's plugins. + + + + Check the option, group, and + plugin URL parameters. If the option is not com_ajax, + and/or the group and plugin don't match your plugin do not return + anything. This protects other people's plugins from getting + interference by your plugin. + + + + Do take into consideration the context of the call to your plugin. + If it's a third party service calling your plugin through com_ajax + directly, not by redirecting the user's browser, you will not + have the same session as the user. Therefore, you cannot + retrieve any session data. There are workarounds to that. You could store + data in a temporary table, under a randomly generated key, and pass that + key to the third party service (provided that it passes that key back to + you). If the user's browser is redirected back to a com_ajax URL do + remember that you MUST redirect the user to a page of the site that makes + sense. Otherwise, they will reasonably assume something got broken. + + If you are relying on GET or POST data to do something you MUST + assume the worst: this is invalid data, sent by a malicious user trying to + hack the site unless positively proven + otherwise. This is an integral part of the software design + principle called defensive + programming. It's not paranoia, it's a fact of + life. People will try to find exploits in + your code. White hats will do that to help you improve the security of + your software. Black hats will do that to break your software for profit + and/or fame. It doesn't matter if your software is a niche plugin used on + exactly one site that barely anyone visits. This does not mean someone + won't try to hack it, it only means it will most likely take longer. So, + as the old Russian proverb goes “trust, but + verify”.