I observed the following behaviour in the menu - help interaction. After the updated menu backend, the help hook gets the following $section values for the paths:

 $_GET['q']                        | hook_help() $section parameter
-----------------------------------+-----------------------
node/1/view                        | node/%
node/1/edit                        | node/%/edit
user/1                             | user/%
admin/build/block/list/chameleon   | admin/build/block/list/chameleon
admin/build/block/configure/user/1 | admin/build/block/configure 

This is not correct, we cannot access the full value of the current path.

Several people suggested that we should just get values from arg() and use it or rely on direct $_GET['q'] usage. Well, if we would always return help for the current page, then we would not need the $section argument at all, and we would be better getting rid ourselfs of it. But it is there, as we are not only using the help hook to get help for the current page. One example in core is to get a list of modules presented in the menu with module level help.

So hook_help() should get better information about the path it is required to return help for. Well, pwolanin people suggested that we should just pass on the whole menu item array, so the help hook can grab what it needs from that array. So I did a look at the actual menu array on the admin/build/block/configure/user/1:

array(23) {
  ["path"]=>
  string(27) "admin/build/block/configure"
  ["load_functions"]=>
  string(0) ""
  ["to_arg_functions"]=>
  string(0) ""
  ["access_callback"]=>
  string(11) "user_access"
  ["access_arguments"]=>
  string(35) "a:1:{i:0;s:17:"administer blocks";}"
  ["page_callback"]=>
  string(15) "drupal_get_form"
  ["page_arguments"]=>
  array(3) {
    [0]=>
    string(21) "block_admin_configure"
    [1]=>
    string(4) "user"
    [2]=>
    string(1) "1"
  }
  ["fit"]=>
  string(2) "15"
  ["number_parts"]=>
  string(1) "4"
  ["tab_parent"]=>
  string(0) ""
  ["tab_root"]=>
  string(27) "admin/build/block/configure"
  ["title"]=>
  string(15) "Configure block"
  ["title_callback"]=>
  string(1) "t"
  ["title_arguments"]=>
  string(6) "a:0:{}"
  ["type"]=>
  string(1) "4"
  ["block_callback"]=>
  string(0) ""
  ["description"]=>
  string(0) ""
  ["position"]=>
  string(0) ""
  ["weight"]=>
  string(1) "0"
  ["file"]=>
  string(0) ""
  ["href"]=>
  string(27) "admin/build/block/configure"
  ["access"]=>
  bool(true)
  ["map"]=>
  array(6) {
    [0]=>
    string(5) "admin"
    [1]=>
    string(5) "build"
    [2]=>
    string(5) "block"
    [3]=>
    string(9) "configure"
    [4]=>
    string(4) "user"
    [5]=>
    string(1) "1"
  }
}

The obvious problem with this construct is that we only need a very small portion of it (the *actual* path), but it is not present as-is in the structure, we would need to join('/', $item['map']) to get some of it. One option is to have this already in the array redundantly. This option should not be taken lightly though. The menu item array includes a copy of the whole node for example on node pages. Well, obviously that might be useful for some help stuff, but most of the time it is not required, so why pass on this data in all cases?

The other problem with this construct is that as mentioned above, the help hook is not used to present help text for the current page only (thinking that goes against the $section parameter), so if we replace $section with $menu_item, we would restrict the help hook to only return help for menu items. It would not be possible to just return help for simple strings.

Here is a simple proposal:

- passes on $menu_key as the second and optional parameter to hook_help(), so if a menu item is used to get the help, that can be passed on
- renames $section to $path, and passes $_GET['q'] on, if we look up for a specific path
- adds TODO comments for stuff chx said he will look into
- example conversion on aggregator and block modules

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

dmitrig01’s picture

Status: Needs review » Needs work

I think hook_help gets just the right amount of information. If I wanted some help text on node pages, I don't really want per-node help text
same with most of your examples

 $_GET['q']                        | hook_help() $section parameter     | comments
-----------------------------------+------------------------------------+-------------------------
node/1/view                        | node/%                             | I don't want per-node help text
node/1/edit                        | node/%/edit                        | I want help text for all nodes, not just one
user/1                             | user/%                             | Same goes for users
admin/build/block/list/chameleon   | admin/build/block/list/chameleon   | Here I would rather it be admin/build/block/list/%
admin/build/block/configure/user/1 | admin/build/block/configure        | Here I don't want per-block *config* settings

And if you really want to go against this, use $_GET['q'] ;)

pwolanin’s picture

Steven asked that the local tasks code be refactored to eliminate redundant code (if possible). Such a change to that function could be combined with allowing it to store/return the correct "highest" path for a default local task. Since the ideal fix for hook_help would depend on that change, should I try to roll a patch that combines the two?

Gábor Hojtsy’s picture

dimitrig01: maybe *you* did not want to add help text to those places, but maybe there are core patches (I know at least of one) blocked because it is not possible to add help text granularly. How many times should I describe that if we have the $section parameter, it implies that the help hook is used for more then just asking for the page level help one time on a page view? And it is not merely implied, it really is used at least two other places additionaly to asking for page level help texts (building the help menu items and printing "more help..." links when required). You advocate hacking with $_GET['q'] in core modules in the help hook? Is this the ultimate solution?

pwolanin: let's see that patch. If it gets too big or unrelated, then it needs to be broken up. If the issues are really closely related, then they will be broken up anyway. (I don't see what you are up to yet with local tasks, the block configure pages are not local tasks for example as far as I know).

pwolanin’s picture

Status: Needs work » Needs review
FileSize
60.43 KB

ok, reworked menu.inc a little so that we can get the information we want - the router path of the highest level default local task (which is just the router path for a non-tab or a non-default local task).

changed every module, I think, to conform reasonably to this, and in a few cases changed hook_help to take advantage of the wildcard $router_path. please review/test.

Gábor Hojtsy’s picture

Title: Help hook gets reduced information » Clean up hook_help() by providing real Drupal and router paths

Well, this looks like a *very good* development! Looks very good.

A few remarks:

1. You did include hook_help($path, $router_path = NULL) in most modifications, but the help, php, ping, poll, search, syslog, throttle, tracker and upload modules does not include the $router_path argument at all. Other help hook implementations include the $router_path even if they don't need it. I think it should be there on all functions for consistency.
2. This looks like this patch makes help hook writes lives an order of magnitude easier :)

-  if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) == 'outline') {
+    case 'node/%/outline':

But! Chx noted that we should really see the menu key here, ie. "node/%node/outline", so we can use the same keys in the help hook and in the menu hook. I understand it will not be a router path then, but a menu key. That would be the extra mile to go here IMHO to make this even more sexy for developers.

3. Staying at the book module example (but this happens with others too), it looks like you divided the help hooks into a part dealing with $path and one with $router_path... The second part deals with paths with variable parts, but it also deals with constant parts. In cases like 'admin/content/book' and 'admin/content/book/orphan', does it make any difference to switch() against the $router_path and not the $path? As far as I understand there is no difference. What about separating by "has dynamic parts" == switch against $router_path and "static path" == switch against $path? Does that make sense, or there is some deeper reason to separate as the current version in the patch?

4. Slightly related. Although you killed of most (nearly all?) arg() calls in help hooks, which is a good cleanup, you did move the "admin/build/themes/settings" to the $path switch. This might be because the menu system has no router definition for this with the ending path part. Would it make sense instead to add this key to the menu with an ending % part, so we can simplify the handling of it? Just as it is with "admin/settings/filters/%"?!

All-in-all I think this is a good direction, simplifies help hooks a lot and even makes some new things possible, so thanks!

pwolanin’s picture

What you described as the menu "key", like node/%node/edit, does not exist except in hook_menu - it is not stored in the {menu_router} table and is not available in the menu item, so I think the router path is the natural choice.

I'll have to look at the specific things you point to - I ran a little low on steam in converting some of the function args, I'll admit ;-)

In general, it's better to use the $router_path if possible, this insures that the user sees the same help message even if they are on the path of a default local task (e.g. node/1/view) or even if they've somehow manged to append an extra argument to the end of the URL.

There are certainly a few cases where addition of a wildcard to a router path might make sense, but I was a little wary of feature creep (which I'm ofter accused/guilty of),

pwolanin’s picture

FileSize
64.03 KB

ok, fixed all hook_help args for the sake of consistency. Should be good to go.

chx’s picture

I was probably misread. We can't have %node however nice it'd be -- or we can but it'd very difficult to reconstruct. I also believe it's good to go.

Gábor Hojtsy’s picture

Well, well... I don't think it is easy to distinguish between what should I put into switch($path) and switch($router_path)... Seems like most of the modules would have both switches, and the first switch mostly contains one item, the one for the module level help page. So this seems like a pattern which we enforce on people, but which could be simplified.

There are really three types of paths we display help for:

- the admin/help#$modulename pseudo-path
- router path based shortcuts, like common help for default local tasks and parent tabs, or node/%/edit page help, etc...
- help based on full path, like node/1/edit help set by some contrib module, or admin/build/block/configure/user/0 help, as it is not available as a menu key

I looked into how Drupal 5 handles the help hook exactly, and it simply passes on $_GET['q'] as the help $section parameter. So what is new in Drupal 6 is that we can display help for the router path, not only the pseudo-path or $_GET['q']. But this also makes it more complex, so we *need to choose* between what we need to return help for.

Seems like we are pushing to use the pseudo-path and the router path if possible, and only resort to full path based trickery if the two simpler cases are not adequate. So why not make *that* way of thinking simple?

I'd say the first parameter of hook_help() should be the pseudo-path and the router path folded into one value (these complement each other, there is no overlap). How to name this is a good question. $section was very odd before, $path might be usable here, and it is already in the patch :) Then the second (optional) argument could be the $_GET['q'] value for the request, if given (that is, if the first argument was a router path). This second argument could be named $full_path or something similar, I would even say $q, but I bet Dries would not like that one :)

So most help hooks would be a switch on the first parameter only, and I would only need to think about the special cases, if $full_path was specified. And then never use arg(), but use $full_path instead in a preg, as that is what specifies the path to look up help for. I think that would simplify the help hook to a Drupal 5 level of understandability, while gaining the good of building on router paths. (I would default $full_path to an empty string to easily use it in preg_match(), without needing to check whether it exists or not).

BTW I discussed with chx that we cannot do better here then using % for all placeholders, without object names.

PS. forgive me that I started off this into a complicated direction, and just trying to help you move the patch to a simpler solution.

PS. search module also creatively makes up a pseudo-path, using the help hook as a callable message store (another sign that it does not return help text only for the current page)... this kind of creative thinking might be present in contrib modules... I am not entirely sure, whether I like this approach or not, a custom callback function to retrieve the reusable help text might be overkill for this task, so reusing the help hook was probably easiest for whoever added that code.

pwolanin’s picture

I'm not sure it's a good idea to pass the router path and the pseudo path in the same variable. But I agree it's sill to always have to switch blocks.

Gábor Hojtsy’s picture

Well, previously (D5) the full path and the pseudo path was in the same variable. Now we have the router path to simplify our lifes a bit, but we still need the fill path for more sophisticated help. So it seems to be logical to merge the new primary values.

pwolanin’s picture

Ok, so you suggestion is that the first variable be still called $path, and be either the router path or a help module pseud-path, right? The second, optional, parameter willjust be passed in $_GET['q']? Or would it make sense to pass here a path map - i.e. an array of the exploded path components (so $map[0] == arg(0))?

Gábor Hojtsy’s picture

Yes, I advocate that. The core use cases might better do with the array structure as with arg(), but they might be rewritten to use preg() which would use a regex instead of multiple array element tests. This is probably a matter of taste. I am not sure what other core maintainers would say, so it might be a safe bet to keep the arg() like format and pass on an array, so the philosophy behind existing code is not shifted.

pwolanin’s picture

FileSize
53.58 KB

Ok, here's a new patch, passes an array corresponding to arg() as the second parameter - note that the elements may be empty strings, but they should always exist.

Note, for a path like 'admin/build/block/configure/user/0'
I would suggest that the hook_help look something like:


function user_help($path, $arg) {
  switch ($path) {
    case 'admin/build/block/configure':
      if ($arg[4] == 'user' && $arg[5] == '0') {
        return '<p>'. t('Here is help for the user login block.') .'</p>';
      }
      break;
  }
}
pwolanin’s picture

FileSize
57.96 KB

patch was missing the second argument for hook_help when it's called from help.module or system.module.

Added additional code comments, and made minor corrections to menu module help.

Wim Leers’s picture

How it all works is out of my league (for now... !), because of my lack of knowledge of the new menu system.

So my review is limited to just testing if it all works and I can confirm it does.

If anybody else reviews it: the " administration pages" thingie seems strange, but another patch will make sure that part works as well.

pwolanin’s picture

This is the relevant issue for fixing the admin-by-module code: http://drupal.org/node/136386

eaton’s picture

This appears to be working nicely. I've never worked too closely with the help system, but smoke testing and poking through the code revealed no problems that I could find. Making it easier to provide granular help? Definitely a good thing.

I'll take a closer look at things -- if no one has RTBC'd it by late tonight I will.

Gábor Hojtsy’s picture

This patch is looking really good, but:

1. The filling of $empty is repeated too many places. It is also a computable value, so why not have a helper function which automates it, and use it when needed?

/**
 * Generates empty elements for the $arg array in the help hook,
 * so we can use them without checking the existence of values.
 */
function drupal_help_args() {
  return array_fill(0, MENU_MAX_PARTS + 1, '');
}

2. Some modules are missing from the patch, like openid.module and translation.module.

The patch seems to be RTBC if these two issues are fixed.

pwolanin’s picture

FileSize
59.35 KB

hmmm, for some reason $ cvs update wasn't giving me the new modules. They should all be included now.

started with your function as a model, but you had a 2-line doxygen, and it makes sense to also be able to use this with arg(NULL).

Gábor Hojtsy’s picture

Status: Needs review » Fixed
FileSize
62.7 KB

Well, I lamented, lamented and lamented whether $args would be better then $arg... But at the end, I turned to the "familiarity with arg()" argument, so renamed drupal_help_args() to drupal_help_arg() to be consequent. Also fixed a remaining $router_path hook help param in the patch, renamed $empty to $empty_arg at all places, $empty looked very odd. Also removed two now unneeded line from locale_help() and removed an extra line from comment_help().

All-in all, I committed the patch attached. Thanks for your work!

This change should be documented in the module update guide!

pwolanin’s picture

initial documentation update: http://drupal.org/node/114774#hook-help

However, the docs in CVS need to be updated.

Anonymous’s picture

Status: Fixed » Closed (fixed)
dvessel’s picture

subscribing

NancyDru’s picture

Status: Closed (fixed) » Needs work

See #22 - the docs for D6 have not yet been changed.

pwolanin’s picture

Project: Drupal core » Documentation
Version: 6.x-dev »
Component: base system » Documentation in CVS
Assigned: Gábor Hojtsy » pwolanin
Status: Needs work » Needs review
FileSize
2.7 KB

ok, here's a draft patch for the CVS docs. I'll commit soon if no one has comments/corrections.

keith.smith’s picture

Note that there is an Aldo that probably should be an Also.

pwolanin’s picture

FileSize
2.75 KB

ok - well that's an easy fix

Gábor Hojtsy’s picture

Status: Needs review » Reviewed & tested by the community
FileSize
2.75 KB

Also fixed a capitalization issue in the beginning of the $path docs. IMHO feel free to commit. Thanks.

pwolanin’s picture

Status: Reviewed & tested by the community » Fixed

ok - CVS docs updated

Anonymous’s picture

Status: Fixed » Closed (fixed)

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