Work In Progress

This book is currently work in progress. Some sections are not yet written. Thank you for your understanding!

Modules and com_ajax

Sometimes, you need to have your modules return data asynchronously 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 extension types (modules, plugins, and templates) handle requests and return results either as JSON or as any arbitrary format.

For modules, you would need to load a URL like this: index.php?option=com_ajax&module=mod_example&method=something&format=raw. For backend modules you obviously need to use administrator/index.php.

The way this works is that com_ajax will create an instance of the module's DIC (that is to say, use the code in our module's services/provider.php file), instantiate the helper that has the name ModulenameHelper, where Modulename is the name of our module without the mod_ prefix (e.g. Example for mod_example), and call the method MethodnameAjax where Methodname is the value of the method URL parameter. In the example above, it would load the ExampleHelper helper of mod_example and call its SomethingAjax method.

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

com_ajax does not go through the module's Dispatcher. As a result, you do not have access to the module parameters, nor can you push any Joomla! object (such the application object, the input object, etc) to the Helper.

If you need any module parameters to be used when handling an AJAX request you have to send them as part of the request (as GET or POST parameters), store them in the session, or pass the module's ID as part of the request (as a GET or POST parameter). Each method has limitations and security considerations:

  • Passing configuration as GET or POST parameters is unsafe. Anyone can read them. Passing secrets, or filesystem paths, is detrimental to security. Moreover, any data read by this method must be ASSUMED to have been tampered with by a malicious actor who is trying to hack your site. Do not make any assumptions about data types, ranges, or that the data will even make any sense. For these reasons, using this method is VERY STRONGLY DISCOURAGED.

  • Storing data in the session does not have the security considerations of passing data through GET or POST, but you must keep in mind that a user may try to open several pages at once, each one with a different instance of your module. This means that the data read from the session may be inconsistent and irrelevant to the current page. Depending on what your module does this could have security or privacy implications.

  • Passing the numeric module ID is much safer. You can very easily try to load a module by that ID and determine if it exists, if it's published, and if the current user should be able to see it (access level check). You can then load its parameters into a Joomla Registry object and process data in your code.

Generally speaking, having modules do AJAX is a problematic idea. While there are very few good use cases where this makes sense (and they all involve data which is always meant to be public), the majority of the uses cases I have seen in the real world display a blatant disregard for security and privacy. If you need to do complex processing maybe what you are doing does not belong in a module, but a component.

And, please, PLEASE, do test your modules with use cases that involve multiple but differently configured instances of your module on the same page, or what happens if you open three or four pages with differently configured instances of your module in very quick succession (open all pages before the first AJAX request has the chance to be executed by the browser), ideally with users with different privileges logged in. You will be surprised at how many problems you will catch this way.