?
avatar Napoleon-BlownApart
Napoleon-BlownApart
22 Jan 2017

This could be considered a feature request.

I have implemented an Inheritable property for menu items that is controlled by the standard binary toggle switch in the Menus:Edit Item -> Details screen. The property determines if higher access levels inherit the menu item.

For example:
With a "Register" menu link (whose access is set to Public with Inheritable NO), a public visitor to the site will be able to register, but once they have activated their account and been upgraded to a Registered user, they will not see the "Register" link while they remain logged in.
Had the Inheritable property been left ON (default), the menu item's behaviour would have been standard Joomla behaviour, and the Registered user would still be able to see the "Register" link (unless a separte menu was created for registered users or some other hack was implemented).

From various posts I have read concerning this issue, I believe this to be a simple, elegant, unobtrusive solution to this particular problem. It would be nice if a future release of Joomla incorporated this change, or something similar.

The details of the changes are below. The changes are, to the best of my knowledge, backwards compatible, but if you choose to implement/try these changes, you should study the surrounding code to be sure you are putting it in the right places. Any and all feedback are welcome!

Cheers,
Nap.

The proposed code can be seen in the Joomla Extension sub-forum:
https://forum.joomla.org/viewtopic.php?f=708&t=945868&p=3457261#p3457261
I have included the code in the post below.

avatar Napoleon-BlownApart Napoleon-BlownApart - open - 22 Jan 2017
avatar joomla-cms-bot joomla-cms-bot - change - 22 Jan 2017
Labels Added: ?
avatar joomla-cms-bot joomla-cms-bot - labeled - 22 Jan 2017
avatar Napoleon-BlownApart
Napoleon-BlownApart - comment - 24 Jan 2017

I have now worked out/understood the ACL system enough to be able to show menu items to selected groups but not others. However it's taken two weeks of failures, extensive google searches, protracted forum posts on the subject, and a lot of frustration and confusion. Judging by comments I've seen, there are a lot of Joomla admins who are struggling with this seemingly simple objective. As testament of this, I refer to the webinar hosted by CloudAccess.net available on YouTube at https://www.[youtube].com/watch?v=gyBhsMFFMBQ

Frankly, I found implementing the Inheritable property easier than understanding Joomla's ACL and achieved what I needed.

Whilst the proposed feature is not as flexible as the Joomla ACL arrangement; ie a group specific menu item cannot be assigned to any other groups, the Inheritable property is very easy to understand and gets the job done without compromising the functionality of the getItems() methods in the JMenuSite class (in libraries/cms/menu/site.php) and the JMenu class (in libraries/cms/menu/menu.php). With the Inheritable property's default setting (inheritable), the menu item abides by Joomla's normal menu item semantics.

The feature is also transparent if it not used, does not add any additional DB hits and only requires one additional call to JFactory::getUser().

On the basis of simplicity, I urge the Joomla developers to consider this feature for future releases.

Cheers,
Nap

The code to implement the Inheritable menu item property follows below:

The affected files are:

  • <JPATH_ROOT>/libraries/cms/menu/site.php
  • <JPATH_ROOT>/libraries/cms/menu/menu.php
  • <JPATH_ROOT>/administrator/components/com_menus/views/item/tmpl/edit.php
  • <JPATH_ROOT>/administrator/components/com_menus/models/forms/item.xml
  • <JPATH_ROOT>/administrator/language/en-GB/en-GB.com_menus.ini

In addition to changing the above files, the following SQL statement needs to be executed:

ALTER TABLE `jos_menu` ADD `inheritable` TINYINT( 3 ) NOT NULL DEFAULT '1' COMMENT 'Determines if menu item is inheritable by higher group access levels.' AFTER `access` ;

Note jos_ in the above SQL is the Joomla table prefix.

File Changes

In file: <JPATH_ROOT>/administrator/components/com_menus/models/forms/item.xml, insert the code below after the access field element is closed, approx line 138:

  <field
     name="inheritable"
     type="radio"
     label="COM_MENUS_ITEM_FIELD_INHERITABLE_LABEL"
     description="COM_MENUS_ITEM_FIELD_INHERITABLE_DESC"
     default="1"
     class="btn-group btn-group-yesno"
     filter="integer">
     <option value="1">JYES</option>
     <option value="0">JNO</option>
  </field>

In file: <JPATH_ROOT>/administrator/language/en-GB/en-GB.com_menus.ini, (or your language's equivalent) insert the two declarations almost anywhere in the file:

COM_MENUS_ITEM_FIELD_INHERITABLE_DESC="Determines if this menu item can be inherited by higher group types."
COM_MENUS_ITEM_FIELD_INHERITABLE_LABEL="Inheritable"

In file <JPATH_ROOT>/administrator/components/com_menus/views/item/tmpl/edit.php, insert the line below after 'access', approx line 130 where the array $this->fields is declared. Note the required comma and the single quotes.

           'inheritable',

In file <JPATH_ROOT>/libraries/cms/menu/menu.php, the body of the method public function getItems($attributes, $values, $firstonly = false) (approx line 250 or 270 depending on your Joomla version) needs to be altered. Easier to replace the whole body of the function than explain where to put the required code:

  $items = array();
  $attributes = (array) $attributes;
  $values = (array) $values;
  $count = count($attributes);

  foreach ($this->_items as $item)
  {
     if (!is_object($item))
     {
        continue;
     }
     
     $test = true;

     for ($i = 0; $i < $count; $i++)
     {
        if (is_array($values[$i]))
        {
           if ($attributes[$i] == 'inheritable') {
              if (!$item->inheritable && !in_array($item->access, $values[$i]))
              {
                 $test = false;
                 break;
              }
           } else {
              if (!in_array($item->{$attributes[$i]}, $values[$i]))
              {
                 $test = false;
                 break;
              }
           }
        }
        else
        {
           if ($item->{$attributes[$i]} != $values[$i])
           {
              $test = false;
              break;
           }
        }
     }

     if ($test)
     {
        if ($firstonly)
        {
           return $item;
        }

        $items[] = $item;
     }
  }

  return $items;

In file <JPATH_ROOT>/libraries/cms/menu/site.php, changes need to be made in two places.
1 Make the SQL retrieve the inheritable property setting by inserting m.inheritable, after m.access, (At approximately line 31). Note the required comma. Here is the whole line including the change:

->select($db->quoteName('m.browserNav') . ', m.access, m.inheritable, m.params, m.home, m.img, m.template_style_id, m.component_id, m.parent_id')

2 Add the code below after the access attributes are dealt with (approx line 122 or 163 depending on the version of Joomla you have). Make sure this code goes after the closure of the preceeding elseif statement:

     if (($key = array_search('inheritable', $attributes)) === false)
     {
        $attributes[] = 'inheritable';
        $values[] = JFactory::getUser()->groups;
     }
     elseif ($values[$key] === null)
     {
        unset($attributes[$key]);
        unset($values[$key]);
     }
avatar Napoleon-BlownApart Napoleon-BlownApart - edited - 24 Jan 2017
avatar laoneo
laoneo - comment - 24 Jan 2017

I suggest to do a PR instead long comments which code snippets. Sorry but we developers are sometimes just too lazy to read text. When you come up with a git diff, then you will get our attention ;-)

avatar Napoleon-BlownApart
Napoleon-BlownApart - comment - 24 Jan 2017

Thanks laoneo, I'll do that.

avatar Napoleon-BlownApart
Napoleon-BlownApart - comment - 26 Jan 2017

See pull request: #13766

avatar Napoleon-BlownApart
Napoleon-BlownApart - comment - 31 Jan 2017

I deleted my branch to do a clean restart. What I didn't realise is that github would close my PR. Now I can't open it because it's been 'force pushed' and it seems to me I need to open a new PR . This does not sound very good because all the history will be lost from the previous PR.

What is the best thing to do now?

avatar laoneo
laoneo - comment - 31 Jan 2017
avatar Bakual
Bakual - comment - 31 Jan 2017

Or just create a new PR and reference the old one. That's what usually is done.

avatar Napoleon-BlownApart
Napoleon-BlownApart - comment - 31 Jan 2017

@laoneo : I had read that thread but wasn't quite sure, being not as familiar with github and git to really understand what to expect. So I've done what @Bakual suggested and created a new PR #13817 .

First up, lets see what Travis thinks?

avatar Bakual
Bakual - comment - 31 Jan 2017

I'm closing this as we have a PR

avatar Bakual Bakual - change - 31 Jan 2017
The description was changed
Status New Closed
Closed_Date 0000-00-00 00:00:00 2017-01-31 09:57:41
Closed_By Bakual
avatar Bakual Bakual - close - 31 Jan 2017

Add a Comment

Login with GitHub to post a comment