Letting a Joomla component assign View Levels to Users

June 28, 2015 0 By addshore

So I recently came across an issue with my speedy rewrite of the component that is used to power the SWA website. Here is a quick summary of the issue and how I went about fixing it!

The rewrite maintains information about users and members in tables that it has created. A User, in Joomla terms is not the same as a member of the component. Members of the component should be able to access various different menu items depending on various details of their member profile. Thus extra levels of access for users needs to be decided by the component.

Component defined view levels

At the lowest level access to menu items generally depends on what view levels a user has access to. These are stored in the ‘viewlevels’ table, so my component would need to add some view levels in order to do anything here at all.

To do this I added the following to the install.sql file for my component, meaning these 2 view levels would be added if they did not previously exist (I could probably also do this separately in PHP post installation).

INSERT INTO `#__viewlevels` (title, ordering, rules)
SELECT 'Club Committee', 0, '[]'
FROM dual
WHERE NOT EXISTS (SELECT 1
FROM `#__viewlevels`
WHERE title = 'Club Committee');

INSERT INTO `#__viewlevels` (title, ordering, rules)
SELECT 'Org Committee', 0, '[]'
FROM dual
WHERE NOT EXISTS (SELECT 1
FROM `#__viewlevels`
WHERE title = 'Org Committee');

Now that the view levels exist menu items can be set to require them. Lovely, except for the fact that I am not going to assign these view levels to users directly, thus right now no one can view these menu items!

Component decided view levels

As my main concern was hiding / showing menu items the logical place to start looking was in ‘mod_menu’ which creates the menus in Joomla. As the module was very small it was easy to see that the list of menu items was generated using the code below.

ModMenuHelper::getList($params)

This method then gets a list of authorised view levels for the current user using the code below:

$user = JFactory::getUser();
$levels = $user->getAuthorisedViewLevels();

So of course next we look at JUser::getAuthorisedViewLevels. This method provides some level of caching so that the view levels are only retrieved once per request, meaning less DB casll. But essentially this method gets the view levels from JAccess::getAuthorisedViewLevels. So this is where need a new event!

JAccess::getAuthorisedViewLevels requests all of the view levels currently defined in Joomla as well as all of the groups that the current user is in. A quick look over everything then results in an array of view levels that are assigned to the users groups. This is where we pounce!

Just before the return of this method we add our event:

static $importedSwaPlugins;
if( !$importedSwaPlugins ) {
   $importedSwaPlugins = true;
   JPluginHelper::importPlugin( 'swa' );
}
JEventDispatcher::getInstance()->trigger(
   'onJAccessGetAuthorisedViewLevels',
   array( $userId, &$authorised )
);

return $authorised;

This passes off the array of view levels to subscribers of the onJAccessGetAuthorisedViewLevels event which means that extra view levels can be added according to $userId.

Note that before this event is fired the code imports all plugins of the group ‘swa’ which is where the plugin that we want to subscribe is located! Also note that the import plugin call is wrapped in an IF so that it is only ever called once, this is due to a loop as apparently the importPlugin method calls the this method again (at some point).

We can then use this event to do whatever we want. See what I did at https://github.com/SWAuk/plg_swa_viewlevels/blob/3c78b858c34f4ee5c3a303b05979191847261837/src/viewlevels.php#L19

Final thoughts

There may have been a nicer way to do this but right now I simply do not have the time!

This is a bit of a hack as we are having to change Joomla code by adding an event however it is a very maintainable hack (simply adding the trigger before a method return).

I wonder if I will ever have time to revisit this….