PR-5.0-dev Pending

User tests: Successful: Unsuccessful:

avatar regularlabs
regularlabs
11 Sep 2023

There is no onPrepareModuleList event being triggered in the new module position rendering.
This means that plugins cannot change the list of modules before the modules get rendered, which is possible in previous versions of Joomla.

This PR adds the missing onPrepareModuleList event to the ModulesRenderer class.

You can test this by creating a system plugin.
In the main plugin file, add this method:

        public function onPrepareModuleList(ArrayProxy &$modules)
        {
            if ( ! JFactory::getApplication()->isClient('site') || ! $modules->count())
            {
                return;
            }

            $modules->offsetUnset(0);
        }

Before the patch, nothing will happen.
After the patch, the first module in every module position should be removed from the frontend.

avatar joomla-cms-bot joomla-cms-bot - change - 11 Sep 2023
Category Libraries
avatar regularlabs regularlabs - open - 11 Sep 2023
avatar regularlabs regularlabs - change - 11 Sep 2023
Status New Pending
avatar HLeithner
HLeithner - comment - 12 Sep 2023

not sure why it should not be triggered, it's still in the ModuleHelper

$dispatcher->dispatch('onPrepareModuleList', new Module\PrepareModuleListEvent('onPrepareModuleList', [
'subject' => new ArrayProxy($modules),
]));

avatar HLeithner HLeithner - change - 12 Sep 2023
Title
Adds onPrepareModuleList event to ModulesRenderer
[5.0] Adds onPrepareModuleList event to ModulesRenderer
avatar HLeithner HLeithner - edited - 12 Sep 2023
avatar regularlabs
regularlabs - comment - 12 Sep 2023

@HLeithner The code you are referencing is in a method that is not (always?) called when template positions are rendered.
The whole ModulesRenderer.php looks to be a bit of a mess, to be honest. Duplicate code, different code styles (ways of triggering events, for instance), not clear what methods have what functionality and when they are used...

avatar regularlabs
regularlabs - comment - 12 Sep 2023

@HLeithner Also, that load method does everything on an empty modules array. Which makes no sense to me.

avatar Fedik
Fedik - comment - 12 Sep 2023

The event works as expected:

public function onPrepareModuleList(\Joomla\CMS\Event\Module\PrepareModuleListEvent $event)
{
  $modules = $event->getModules();
  dump(count($modules));
}

public function onPrepareModuleList(ArrayProxy &$modules)

It is wrong use of the event.

There is no onPrepareModuleList event being triggered in the new module position rendering.

No. It is trigering only once before all modules for the page are loaded.

Nthing to fix. I closing it. Thanks for your time.

avatar Fedik Fedik - close - 12 Sep 2023
avatar Fedik Fedik - change - 12 Sep 2023
Status Pending Closed
Closed_Date 0000-00-00 00:00:00 2023-09-12 09:21:29
Closed_By Fedik
Labels Added: PR-5.0-dev
avatar regularlabs
regularlabs - comment - 12 Sep 2023

Why close this... try it out with the plugin... you'll see that you can't change the modules list(s) that are rendered on the page.

Please back up your claim that everything works as it should with some proof.

avatar laoneo
laoneo - comment - 12 Sep 2023

Perhaps we have here a backwards compatibility issue as @regularlabs mentioned that it was working in J4.

avatar Fedik
Fedik - comment - 12 Sep 2023

Then please provide a better description for your issue. What exactly does not work.

The specific event onPrepareModuleList works as expected.

avatar regularlabs
regularlabs - comment - 12 Sep 2023
public function onPrepareModuleList(\Joomla\CMS\Event\Module\PrepareModuleListEvent $event)
{
  $modules = $event->getModules();
  dump(count($modules));
}

When I do that on a clean Joomla 5 installation, it outputs 0. And only once.
The onPrepareModuleList should be called for every module position on the page.

To ask this in a different way:
Can you give me the plugin code to remove the first module in a module position?

(And yes, the answer for all previous Joomla versions is to use onPrepareModuleList)
(And no, this does not work on Joomla 5)

avatar regularlabs
regularlabs - comment - 12 Sep 2023

image

avatar Fedik
Fedik - comment - 12 Sep 2023

When I do that on a clean Joomla 5 installation, it outputs 0. And only once.
The onPrepareModuleList should be called for every module position on the page.

Why do you think that?
It was made to be only once, since this event exists. You can check the origin here #6320

There 2 more events used to clean the modules list: onAfterModuleList and onAfterCleanModuleList you can use them to remove unneded modules.

avatar regularlabs
regularlabs - comment - 12 Sep 2023

I'll look into this.

avatar Fedik
Fedik - comment - 12 Sep 2023

From the history, purpuse of onPrepareModuleList not the same as example onContentPrepare or onContentPrepareForm, it may confuse, but it is what it is.

avatar joeforjoomla
joeforjoomla - comment - 12 Sep 2023

Confirmed. The $modules = $event->getModules(); returns an empty array. We have a problem.

avatar Fedik
Fedik - comment - 12 Sep 2023

We have a problem.

Would be nice if you read the comments. Thanks in advance.

avatar regularlabs
regularlabs - comment - 12 Sep 2023

No, @Fedik was right. Error was on my side (assumptions I was making).

avatar joeforjoomla
joeforjoomla - comment - 12 Sep 2023

But there is a wrong code in the ModuleHelper.
How to set the $modules array now?

Indeed the onPrepareModuleList produces no effects. The ArrayProxy object wraps the $modules array, so it will be always empty when checking with: if (!$modules) {

    ```
    $dispatcher = Factory::getApplication()->getDispatcher();
    $modules    = [];

    $dispatcher->dispatch('onPrepareModuleList', new Module\PrepareModuleListEvent('onPrepareModuleList', [
        'subject' => new ArrayProxy($modules),
    ]));

    // If the onPrepareModuleList event returns an array of modules, then ignore the default module list creation
    if (!$modules) {
        $modules = static::getModuleList();
    }
avatar Fedik
Fedik - comment - 12 Sep 2023

How to set the $modules array now?

This event was made for very specific cases. Not really for average use.
Look the comment in the code that you posted.
Also original PR #6320

avatar joeforjoomla
joeforjoomla - comment - 12 Sep 2023

How to set the $modules array now?

This event was made for very specific cases. Not really for average use. Look the comment in the code that you posted. Also original PR #6320

@Fedik i still don't understand. In J4 i was just loading the $modules list and it worked. Now it is no longer possible.
Can you please let me know how this event should be used then?

avatar joeforjoomla
joeforjoomla - comment - 12 Sep 2023

Look the comment in the code that you posted.

There is no assignment if the onPrepareModuleList event returns an array of modules

So it can't work

avatar Fedik
Fedik - comment - 12 Sep 2023

It is working. A magic ?

avatar joeforjoomla
joeforjoomla - comment - 12 Sep 2023

The only way to have it working would be to set the modules list in the $data variable of ArrayProxy, but it is protected.

Other than this, nothing works. Please check better.

avatar Fedik
Fedik - comment - 12 Sep 2023

Did you tried?
Please show me the code you tried.
Hint: ArrayProxy implements ArrayAccess

avatar joeforjoomla
joeforjoomla - comment - 12 Sep 2023

Of course!!! I'm debugging it now with XDebug.

How can it work if there is no reference to the $modules array?

The ModuleHelper checks the $modules array that always stays empty after the call to onPrepareModuleList:

       if (!$modules) {
            $modules = static::getModuleList();
        }

There is no way to manipulate it as long as it stays within the ArrayProxy object. The & reference is not possible, the $event->setArgument('subject', $subject); is also not effective.

The only thing that works for the reference, is to set again the data property of the ArrayAccess object:

$subject->data = $modules;
$event->setArgument('subject', $subject);
avatar Fedik
Fedik - comment - 12 Sep 2023
$subject->data = $modules;
$event->setArgument('subject', $subject);

What is it man? how do you use it in joomla 4 and 3?

$modules = $event->getModules();
$modules[] = ... add module1;
$modules[] = ... add module2;
...so on
unset($modules[0]); // remove first module
avatar Fedik
Fedik - comment - 18 Sep 2023

@regularlabs a note, I noticed your code:
onPrepareModuleList(ArrayProxy &$modules)
$modules->offsetUnset(0);

If you have this or similar code in your extension please update it to use a regular array (or use an event instance)
We are removing ArrayAccess wrapper for $modules array in upcoming beta.
Thanks!

avatar regularlabs
regularlabs - comment - 18 Sep 2023

Ok, great.

Add a Comment

Login with GitHub to post a comment