Adds weights to the menu black/white list, so that you can set a preferred order in which the available menus will be considered.

I've renamed the blacklist and whitelist variables (compared to the version in #556552) to match the current 6.x-1.x-dev code, however there are changes to the structure of the 'menu_breadcrumb_menus' variable to add the weights, so as it stands you would need to reconfigure the module before usage.

As 'menu_breadcrumb_menus' was introduced in the current -dev branch with no stable release using it, this probably isn't an issue.

Extracted from #556552: Menu weights, black/white list, and memory of menu selection. If I've made an error in splitting out this piece of functionality, please compare against that version (which has been tested).

Comments

jweowu’s picture

Status: Active » Needs review
dboulet’s picture

Status: Needs review » Needs work

Thanks for the patch jweowu. This appears to function correctly, but I think that the user interface for the settings page could use some work.

The 'NEW MENUS GO HERE' section is confusing, and the layout of the menu list with the weight fields is a bit messy with the select boxes displaying under the menu name. I would suggest that we put the list in a table so that the weights display beside each menu name, maybe we could even allow the user to reorder the menus using drag 'n drop, that would be pretty slick ;).

dboulet’s picture

Ok, after a cup of coffee, I realize that the drag and drop functionality was included in your patch, fantastic! The reason that it wasn't showing up is that you forgot to register the theme function that renders the table in hook_theme(). This patch fixes that.

jweowu’s picture

Argh. I figured I might miss something when splitting these smaller patches out, but I didn't think it would be something as trivial as that. Thanks for the catch/fix :)

dboulet’s picture

I'm still questioning the use of the 'NEW MENUS GO HERE' feature. The approach seems a little unconventional, and I'm not sure that it's necessary. I would vote for simplifying the whole interface and scrapping it...unless others think that it would prove to be useful?

jweowu’s picture

The problem is that menus can be dynamically generated.

Most notably by the core book module (each book adds a new menu). Admittedly book.module then calls menu_set_active_menu_name() which over-rides menu_breadcrumb -- see #595280: Ability to retrieve the selected menu. -- but if you're circumventing that issue, then you potentially do want control over where newly-created book menus fit into the ordering.

And books aside, any contrib module could do similar things, so I do think that a menu weighting system needs to provide for the possibility, even if few people end up needing that functionality.

I do agree that it's a bit weird/ugly. We could potentially hide it behind a checkbox (tick the box to enable control over where the new menus will appear, otherwise put them last?) I'm not sure it's such a bad thing to force people to consider it, which is what the current approach does, but that might just be me.

dboulet’s picture

Maybe the answer then is simply to change the wording in the interface to make what the extra item represents clearer to the user. Maybe it could simply be called 'Other':

Menu list:
-------------------------------------------------------------------------------------
| + | Primary links
-------------------------------------------------------------------------------------
| + | Secondary links
-------------------------------------------------------------------------------------
| + | Other (new menus, dynamically generated menus, etc.)
-------------------------------------------------------------------------------------

I still think that this will be confusing for some, because we can never know exactly what those 'Other' menus will be.

Thoughts?

jweowu’s picture

Yeah, if the issue is potential confusion, then any descriptive text that helps people understand is a good thing.

I tried to cover that with "Any menus added subsequently (for example, by adding a book) will be processed at the position of the NEW MENUS GO HERE line", but I guess that could still be confusing; especially to people who are new to Drupal. Your "new menus, dynamically generated menus" line is more descriptive. Maybe we could merge that in like so:

"Any menus added subsequently (new menus, dynamically generated menus1) will be processed at the position of the NEW MENUS GO HERE line"

With footnote:
1 for example, by adding a book

To me "Other" implies that there might be other existing menus that aren't in the list.

I think I just tried to be really unambiguous when I wrote it :) Maybe "New menus" would be enough. We could add a #description to the menu list with additional text about this, to further explain it if necessary.

Explicitly saying that new menus will be processed at that position until menu_breadcrumb is next re-configured might help?

xurizaemon’s picture

+1 - nice work

I see "---NEW MENUS GO HERE---" as basically just a default position. I think we could easily just let this be 0 by default and remove the "NEW MENUS" UI element. Whether a new menu is added via a module interface (new book in book module or via ?q=admin/build/menu/add), the menu is available straight away for re-ordering via this interface.

xurizaemon’s picture

StatusFileSize
new6.75 KB

My thinking with this patch is that simpler UI is better. So I removed the blacklist option (and radio boxes to choose) in favour of whitelist default, and removed the default row also.

Behaviour should be that you will start with primary, secondary and navigation as m_b menus enabled, and used in that order. Devel, admin_menu will be disabled by default. Newly added menus (via admin/build/menus/add or eg book module) will need to be manually enabled in m_b settings to work and will start with a weight of 0 (below the three enabled by default menus).

Are there any scenarios this approach wouldn't work for?

EDIT: Yes, there are scenarios this approach doesn't work for, as illustrated by jweowu below. The attached patch should be ignored.

dboulet’s picture

@xurizaemon, I agree with you that a simpler UI is better, and I like the ideas you present in #10. The problem that jweowu was trying to solve with the "---NEW MENUS GO HERE---" element was that some menus, such as the book menus, are not really menus but are generated dynamically. This means that they will never show up in the list on the Menu Breadcrumbs settings page. jweowu still wanted to have the option of setting the precedence of these menus, for example have the book menu be considered before secondary links, but not before primary links.

We were trying to find a suitable way to have a UI element that represents these menus—I had suggested 'Other', but maybe 'Default' or some other word would be fitting.

xurizaemon, could you please post a new patch created with the cvs diff -up command?

jweowu’s picture

We need both blacklists and whitelists.

Blacklists are needed if you WANT dynamically-added menus to be processed by menu_breadcrumb.

Imagine if every time someone added a new book, you then needed to go and add that book's menu to the menu_breadcrumb whitelist!

The "---NEW MENUS GO HERE---" item is indeed representative of the zero weight, but we need it for the javascript draggable tables functionality -- without it, you have no control over what the assigned weight values actually end up being. Its purpose is to allow the weights generated by the javascript to be adjusted with respect to a known zero position, so that any subsequently-added menus will then be processed in the correct position without re-configuring menu_breadcrumb.

We could possibly look at making that javascript-only functionality. Without javascript, the weight values are visible, and hence the zero position is under manual control.

I'm happy for excess complexity to be made optional -- simpler interface by default, and the extra bits if you ask for them -- but I'd be very disappointed if this stuff was just removed. It's not that confusing. I really just think we need some sensible descriptive text to explain what it does and why.

dboulet: The dynamically-added menus are real menus. Add a book, and its menu will show up in the menu_breadcrumb menu list. But what you said is correct otherwise -- I want users to have some control over the precedence of those menus without needing to visit the menu_breadcrumb settings page every time one is added. Even assuming the user adding the book actually had permission to configure menu_breadcrumb, that would be an absurd requirement.

dboulet’s picture

The dynamically-added menus are real menus. Add a book, and its menu will show up in the menu_breadcrumb menu list.

Thanks for clearing that up. I don't use the book module very often, and had misunderstood.

xurizaemon’s picture

@jweowu, you're right. I hadn't considered the scenario you suggest, where a site might be adding many new books (or other menus) and expecting menu_breadcrumb to respect those hierarchies without any additional effort on their part.

I still have a (UI only) concern with blacklist mode: it inverts the meaning of a checked menu item, so that (depending on whether blacklist or whitelist is in effect) a tick next to a menu may mean don't use this menu.

I feel like that's not a very intuitive interface, and that a better UI might be to replace the combination of radio buttons for blacklist/whitelist mode and a checkbox on each menu with a select on each menu with options "use for m_b" and "don't use for m_b". That makes the meaning of each one quite explicit.

If you think that would be an useful improvement to the UI, I expect that to do this we'd need to update the setting menu_breadcrumb_menus each time a new menu was generated based on the selected option for the default (zero) row ... is there a specific hook for catching new creation of new menus?

jweowu’s picture

That could work (and I like the result if it does).

As far as catching the appearance of a new menu goes, menu_link_save() calls drupal_alter('menu_link', $item, $menu), so I guess we can implement hook_menu_link_alter() and check to see if $item['menu_name'] is known. (Does that cover all cases? I'm not 100% sure.)

Do we need to do that, though?

In _menu_breadcrumb_get_menus() I merge in any existing and previously-unknown menus, and set 'selected' => FALSE.

If instead we set 'selected' to the value of the NEW MENUS checkbox, I think we get the effect we want?

(unless I'm missing something; I've not thought this through thoroughly yet.)

xurizaemon’s picture

You're right, I expect there would be no need to catch the new menu creation as long as it's handled in _menu_breadcrumb_get_menus() anyway. Much better.

dboulet’s picture

One last thing to consider: should we worry about cases where the list of menus grows very large? A large site could easily have hundreds of books, for example. Should we just let the list grow ridiculously long, or should we make use of a pager or something of that sort?

jweowu’s picture

It's a valid point. Sorting across multiple pages becomes an issue, though.

For paging without javascript we could turn items into hidden fields according to the current page number. We don't need to modify the form much, but the user has to ensure that they click submit on every page they modify before going to the next, so... less than perfect.

A javascript-only paging mechanism, might be better. I'm usually happy to assume that most site administrators will enable javascript. That avoids reloading the page, or making any modifications to the underlying form code, and there could be options to dynamically set the page size, or "show me everything".

You still have a real usability problem when you want to drag and drop the menu order, though. Well, you have one already if your menu list is that large, but this seems to make it worse. I can envisage some fancy solutions whereby you can select/collapse groups of menu items based on some criteria, and drag them as a single unit, but I don't want to write that code :) (although... if we made the assumption that books are the only thing really liable to cause this problem... potentially we could do just that. Book menu names all match the same pattern. We could collapse them to a single draggable item by default, and expand on request...)

For my part, I think I'd be happy to defer this to a separate issue. It seems like a can of worms that doesn't need to be opened just yet (but certainly worth looking into later).

jweowu’s picture

That said... how about this as a solution?

Under a collapsed-by-default 'Advanced' settings form field, we have a text area where you can configure regular expressions for aggregating specified menu name patterns, and their 'replacement' title. By default we set this to /book-toc-\d+/Books/, but users can add their own patterns if they have a need.

_menu_breadcrumb_get_menus() filters out menu names that match these patterns, and just returns a single sub-array for each one, probably with some additional properties like 'type' => 'pattern', 'title' => $replacement_title. The array key would be the regexp.

Because that return value feeds the settings form generation, it only generates one draggable item for each pattern. It also feeds menu_breadcrumb_menu_list() which is used to process the menu_links for the current path. menu_breadcrumb_menu_list() itself wouldn't really change. The regex can be used as the key in its return value, and if we require each regex to start and end with the traditional '/' then we can recognise them as patterns.

When processing the menu_links query results, if the current $menu_name is actually a pattern, then instead of the array_key_exists() check, we check for a regular expression match against each of the menu_links.

Just delete the pattern from the settings form to revert to manual control over each book, if for some reason you really need that.

As a bonus, if you had a lot of books and wanted to treat one or two of them as special cases, you could edit the pattern to exclude those menu names, and then you'd get to order them individually from the others.

xurizaemon’s picture

@jweowu, #19 sounds like an ingenious way of handling default settings for sites which have need for handling a lot of menus.

Based on the patch in #3, I've implemented an interface which removes the mention of "blacklist" feature, applies "Enabled" or "Disabled" instead of a checkbox for each menu, and installs the same options on the "Menu Default" (aka zero_row) item, which allows us to discern a default setting for newly created menus.

See attached graphic and patch. This is based on #3, not on my earlier patch in #10.

jweowu’s picture

StatusFileSize
new27.07 KB

Nice one.

I wonder if checkboxes might still be nicer, if we add table headings?

Screenshot attached for comparison.

jweowu’s picture

I'm working on #19.

xurizaemon’s picture

I like the look of the checkboxes too :)

jweowu’s picture

StatusFileSize
new16.4 KB

Here's a first draft of the pattern-matching. It includes #20 and #21.

Now that we're once again only considering menus that are 'selected', we could revise some of the code that used to think that 'unselected' was also potentially important.

In particular, given the use of 'enabled' in our descriptions, perhaps we should change the internal property name from 'selected' to 'enabled'?

It would make sense with the current approach, and make the code that little bit more obvious to read.

jweowu’s picture

StatusFileSize
new16.58 KB

Minor tweaks.

On my to-do list:

1. Some efficiency optimisations. I'm doing far too much work per request at present. I more or less know what I want to change, but the weather is nice today, so it may have to wait :)

2. Decide how (or if) to deal with defunct menus. At the moment, they'll remain in the persistent variable. Are there situations where deleting them from the variable automatically would be bad?

jweowu’s picture

StatusFileSize
new19.84 KB

Improved version attached. Regex matching is now only done the first time a new menu is noticed, or if the settings form is submitted (in which case all menus are re-checked).

I'm still wondering whether unique weights should be enforced in _menu_breadcrumb_get_menus(). I'm not sure that it really makes a difference, but it would look a bit cleaner internally.

Re: my last comments, I'm still not doing anything with defunct 'real' menu names, but I am removing defunct patterns.

jweowu’s picture

StatusFileSize
new19.84 KB

Note to self: It helps to actually test the final changes you make. Introducing bugs in your clean-up pass over the code is just embarrassing. See new attachment for fixed version.

_menu_breadcrumb_get_menus() seems quite large now.

I think breaking out the contents of the if ($unknown_menu_names) { ... } and if ($new_menus) { ... } blocks into separate functions would be sensible.

Otherwise I think I'm done for the time being :) Please do start looking this over when you have time.

jweowu’s picture

StatusFileSize
new20.34 KB

I think breaking out the contents of the if ($unknown_menu_names) { ... } and if ($new_menus) { ... } blocks into separate functions would be sensible.

Done.

jweowu’s picture

Note: make sure hook_uninstall removes any new persistent vars.

jweowu’s picture

I'm looking at the following:

1) Changing the internal 'selected' property to 'enabled' (see #24).

2) Removing defunct menus from the variable, as I can't think of a reason not to do this (see #25).

3) Adding the missing variable_del()s to hook_uninstall() (see #29).

4) Removing the 'zero' aspect of the 'zero_row' entry. I don't believe it is necessary any longer, as all new menus are now immediately noticed and processed. This means there is no particular reason for the default weight to be zero -- we'll just assign new menus whichever weight this row happens to have.

I'll rename it to something more representative of its remaining purpose (maybe 'menu_breadcrumb_default_menu').

After that, I'll set this issue status to "needs review", if no one has any objections.

edit: All done. Will do some more testing later, then upload the new patch.

n.b. You will need to variable_del('menu_breadcrumb_menus'); if you've been testing an earlier version, due to the name change for 'zero_row'.

jweowu’s picture

StatusFileSize
new23.36 KB

Okay, I'm done here. I think this is good to go, so please review.

If you've used a previous version of this patch, or one of the recent -dev builds, or a patch implementing menu black/white lists, then you may have an incompatible 'menu_breadcrumb_menus' variable in your database. You can use the "Reset to defaults" submit button on the settings form at admin/settings/menu_breadcrumb to resolve this conflict (this will clear all menu_breadcrumb variables).

dboulet’s picture

Status: Needs work » Needs review

Good stuff jweowu, here are a few comments:

  1. Instead of having the book regex pattern activated by default, maybe could we display it as an example in the help text. Having the pattern activated by default could potentially confuse people who don't have the book module enabled.
  2. It says on the form that the Navigation menu will be used by default, even if it is disabled. Does this essentially mean that it can't be disabled? In that case should the 'enabled' checkbox be disabled for that menu?
  3. The regex gets inserted into the id and name attribute values of the checkbox form elements, which doesn't validate. For example, the id of the checkbox for the Books menu is edit-menu-breadcrumb-menus-/^book-toc-\d+$/-enabled
jweowu’s picture

Thanks dboulet.

#1 - I'll look into this.

#2 - It's always been the case with this module -- if menu_breadcrumb doesn't call menu_set_active_menu_name(), then it remains set to its default value of 'navigation'. Can we safely set it to a non-existent menu name? (we can't use a real-but-non-matching menu -- aside from anything else, in edge cases there might not actually be one). I decided it was out of scope for this issue, anyhow. We don't want to disable the checkbox, because the enabled/disabled status is still relevant when we're looking for a menu to use.

#3 - D'oh.

xurizaemon’s picture

Great. I wonder if we should clean up the menu_breadcrumb_menus variable? We could try to convert it to the new format but it's probably easier to just zap it and notify people; it's only been available via patch or -dev tarball.

Something like -

/**
 * If people are upgrading from a patched version based on drupal.org/node/303247
 * we'll try and catch it here. Will just notify and clear the variables for now.
 */
function menu_breadcrumb_update_6000() {
  $menus = variable_get('menu_breadcrumb_menus', NULL) ;
  $filter = variable_get('menu_breadcrumb_menus_filter', NULL) ;
  if ( !is_null($menus) && !is_null($filter) ) {
    drupal_set_message(t('Menu Breadcrumb settings have changed recently. Please review the !link and check that the correct menus are selected there.', array('!link' => l('Menu Breadcrumb settings','admin/settings/menu_breadcrumb'))),'warning');
    variable_del('menu_breadcrumb_menus');
    variable_del('menu_breadcrumb_menus_filter');
  }
  return array();
}
jweowu’s picture

StatusFileSize
new28.75 KB

dboulet: Well I've left #1 alone. My thinking is that because Book is a core module, it just makes more sense to handle it automatically. In reality I doubt there are many non-Book applications for the pattern matching, and I doubt there are many situations where aggregating books would not be the desirable thing to do.

If you have no books, then you won't see an entry in the table for them. You'll just see the pattern in the Advanced fieldset, if you open it. I don't want to only set that default value if the book module is enabled, because then we don't handle them by default if someone enables it later on (after we've already set a value for that variable).

I've added a fix for #3, using a copy of Drupal 7's ID attribute sanitizer (which doesn't seem to be in the API docs yet, but it's part of D7's common.inc).

I've also updated the translation .pot file using http://drupal.org/project/potx

xurizaemon: I started pondering that update function, but realised I need sleep instead...

jweowu’s picture

Adding pre-patched (#35) tarball of module to help testers who aren't used to patches/CVS.

Please note the comment in #31, though -- for the moment, that still applies.

jweowu’s picture

I've added in the hook_update function. I removed the code that checked for the menu_breadcrumb_default_menu array key every time, and added it if it wasn't there. I'd really only put that in because of the name change from zero_row, but this way we can check for it in the update function (which I've modified accordingly), and do fractionally less work on each request.

(Note to any new testers: This deals with #31, if you run update.php after installing.)

I added a CHANGELOG.txt file as well.

I had another thought while testing... should we aggregate the known problematic menus in a pattern, by default?
/^(admin_menu|devel)$/Known problem menus/

They would remain disabled by default, but this would make it more obvious that they shouldn't be enabled.

xurizaemon’s picture

IMO it's probably better just to add a message in the description saying "you almost certainly don't want to enable admin_menu or devel here". Adding a pattern may confuse people by making it unclear which of two methods they should use to enable / disable menus.

Or: if someone is already confused/uncertain, documentation via regular expressions probably won't help them :)

jweowu’s picture

if someone is already confused/uncertain, documentation via regular expressions probably won't help them :)

Heh. Yes, I had some reservations along those lines myself.

Never mind. I think I'm probably more enamoured of the idea because it would have meant there was more than just one common application for that pattern matching code :) Maybe we could just throw it into the readme file as an example usage.

xurizaemon’s picture

have committed patch from 37, will be available in the upcoming 6.x-1.x-dev

dboulet’s picture

Status: Needs review » Fixed

I suppose that means that the issue is fixed.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.