Problem/Motivation
This issue is spun off from the patch on #2351991: Add a config entity for a configurable, topic-based help system, which in turn became a sandbox module:
https://www.drupal.org/sandbox/jhodgdon/2369943
One part of that issue was an overhaul of the admin/help page, to make it more flexible. The idea is that there are actually multiple Core and Contrib modules that provide "help" (broadly defined as "useful information that is helpful for people new to Drupal or some of its systems" or something like that). It would be useful, therefore, if the Help page would list all available help, instead of what it is doing now, which is just listing module overview pages provided by hook_help() in installed modules.
If the Tour module participated in this, it would also take care of providing #2205801: Create Available Tours block, but not as a block as proposed in that issue.
Proposed resolution
a) Add a plugin that allows modules to provide sections to the help page.
b) Have both the core Help module and the core Tour module implement this plugin, so that the Help page will list both hook_help() module overview pages, and available tours.
c) As a side effect of this, update how the markup is provided for the admin/help page, so that it is all in the theme layer instead of being hard-coded in the HelpController class. See also #2337317: Replace help page layout CSS with reuseable layout classes, which went part-way down this path but didn't really do enough.
Remaining tasks
Make patch and commit.
User interface changes
The Help page will be more flexible, and show more help, including Tours and potentially help from contrib modules that choose to provide help page sections (Advanced Help could do so, for instance)
Before

After

API changes
New plugin type is added.
The markup and text of the admin/help page changes slightly.
Markup of the admin/help page is moved into the theme layer instead of being hard-coded in HelpController.
Data model changes
None.
| Comment | File | Size | Author |
|---|---|---|---|
| #81 | interdiff.txt | 2.53 KB | jhodgdon |
| #81 | 2661200-81.patch | 36.28 KB | jhodgdon |
| #3 | oldhelppage.png | 96.78 KB | jhodgdon |
| #3 | newhelppage.png | 116.54 KB | jhodgdon |
Comments
Comment #2
jhodgdonAnd by the way, I'm working on a patch now...
Comment #3
jhodgdonHere's an initial patch, with tests, docs, etc.
Also here are screen shots of the new/old help pages (they are kind of large images so just linking not embedding in this comment, hope that is OK). Differences to note:
- Slight change in wording for "For more information" sentence in Getting Started.
- "Help topics" section on old becomes "Module overviews" (and description text line also changes accordingly). Pay no attention to the differences in the list of topics here though... The screen shots came from two different sites, and I had more modules enabled on the "New" site than on "Old". Sorry about that.
- New page also lists tours
Screen shots -- new:
Old:

Comment #5
jhodgdonFixing test failures. I had to add the template to Stable, fix the NoHelp test (it was looking for the old test on the admin/help page) and the help.module file (it was skipping modules that didn't provide a main help topic, which violated the HelpTest test).
Note that the interdiff file doesn't include the added template in Stable -- it only has the other two changes. Hope that is OK.
Tests pass locally now; should be OK to review.
Comment #7
jhodgdonDang, sorry, uploaded wrong patch file there, without my updates from the interdiff (forgot to git add -u them, oops).
Comment #8
jhodgdonComment #9
jhodgdonTests pass this time. ;)
Comment #10
larowlanI love this idea - great work @jhodgdon -
some observations:
I was of the understanding that we were trying to avoid adding new hooks for discovery - we worked hard in Drupal 8 cycle to remove most of them in favour of plugins. Is that still the case? Moving to a plugin would make this more discoverable and self-documenting as we could provide an interface:
and possibly a base class for the permission bit, which would be pretty common.
This could be a service, with injected module handler, link generator and route match, which would allow unit-testing
This is nice
If these were plugins, we could use ContainerFactoryPluginInterface and inject the tour definition and the storage handler
We need to check access here, not all routes will be visible to every user that has access to help pages, and hence not all tours will be available.
We need to add test coverage for that scenario too.
In what circumstances would this occur? Could we document them?
Comment #11
jhodgdonThanks for the review!
In regards to item 6, I did document in there I think that the problem is that some routes require parameters. So the try/catch is to find a route that doesn't. For instance, you might have a tour on the foo/add and foo/{foo}/edit pages. The edit page would exception out (it has a parameter) and the add page would work without parameters. Anyway I'll fix the comments so this is clearer.
Good idea on the plugin vs. hook. I'll work that up...
Comment #12
jhodgdonOK! I have implemented a plugin system instead of hook for the help page sections. I think all the review comments in #10 have been addressed (most were related to that). One note: what I decided to do for the Tours section permissions check was go ahead and list them but not make links if the user doesn't have permission to visit that page. This functionality is also tested in TourHelpPageTest. Note that all modules are shown in the Module Overview section too, even if the user doesn't have admin permissions on that module, so it is kind of parallel to list the tours but I agree that providing 403 links to the admin pages with the tours on them is probably not useful.
The screen shots are exactly the same as in #3. Only the underlying functionality has changed (extensively). Tests pass locally; let's see what the bot says.
I've included an interdiff file, although since it's nearly as large as the patch (almost everything changed), I am pretty sure it is not very useful. I recommend looking at the patch file instead, but whatever. ;)
Comment #13
jhodgdonTests passed; let round 2 of reviews begin! (thanks in advance!!)
Comment #14
larowlanThank you @jhodgdon for considering my input - such a quick turnaround too!
Here are some observations, but I think this is very close.
I think you can set a default here
And then those that don't need anything more can simply omit this parameter in their annotation. That's how doctrine annotations work outside Drupal, not sure if there is some Drupal specific stuff that breaks that, but in general doctrine/annotations support that
Sometimes #links is an array, sometimes it is a string. Given we pass this into the alter hook (hook_help_section_info_alter) - should we use a render array for the 'There is currently nothing...' too, so the format of #links is consistently an array?
'Empty array if there isn't any' sounds a bit abrupt. Suggest 'If the returned topics don't require cache metadata, return an empty array' or similar? (docs isn't my strongest point, so defer to your expertise)
Do we normally include things like that in documentation 'hopefully' :). I can confirm that ModuleInstaller::uninstall flushes all cache bins -
So I think we can be a bit more definitive in our wording :)
Random observation: there are some examples in core where we are more explicit about the dependencies. I.e. instead of injecting the whole entity-type manager, we only inject the elements which the plugin truly depends on - which is the tour storage handler and the tour entity type. See \Drupal\comment\Plugin\Menu\LocalTask\UnapprovedComments for example. In those instances the constructor argument contains the explicit dependencies and the static factory takes care of fetching those directly from the entity-type manager, rather than injecting the whole shebang. This means you only need to mock the real dependencies instead of the behaviour of the entity-type manager if writing a unit test. Probably best to get a third opinion on that, I recall @timplunkett having an opinion on it in other issues, but can't remember which way he preferred.
random observation, if we injected the link and url generators instead of using the static factories, we could unit-test this method, instead of a web test - take this observation with a grain of salt, won't hold issue up on it
is the additional space normal here?
[ [?Comment #15
jhodgdonThanks for the review in #14! Your points:
1. I wouldn't want the default to be 'access administration pages'. The default would want to be '', because the admin/help page already only is visible for 'access administration pages', so we don't need that permission. Anyway, I made that default and updated the docs.
2. We do not actually send the output of the page into an alter hook, beyond the generic theme/render hooks that all render arrays get passed to. hook_help_section_info_alter() is for the plugin definition arrays (which basically means it gets the information in the @HelpSection annotations).
3. Updated docs. This reminded me that the tours section should probably be given the user context as it is checking permissions, so added that too.
4. Hah. Updated docs. There's an edge case possible where a module adds hook_help() when it didn't have it before, but developers doing that would know they need to clear cache if so, and if it's an update they downloaded they should run update.php which also clears the cache. So. Took out 'hopefully'. :)
5. Hm. Deferring that (if necessary) for now. I see what you're saying, don't see a compelling reason to do it for the moment. I would not expect this plugin class to be instantiated except when building the admin/help page, and I don't see a compelling reason to do it one way vs. the other, since both pieces (cache and topics) will be called for, one after the other, in any case.
6. I didn't make this change. The reasoning I used:
- The static Link and Url calls return you objects that are lazy-evaluated at render time, unlike the URL and Link generator classes. In the HookHelpSection class, that is desirable, I think, to leave the links as late as possible to render.
- In the Tour class, the link is rendered immediately to verify there is not an exception, so we could use the link generator service. However, the URL object is also used -- we call the ->access() method on it. It would mean injecting at least one and maybe several other services to replace everything the URL object is doing, and I think the code would be much less clear, because the object returned by the URL service is not something you can pass into the Link generator (which is expecting a regular Url object, not what the URL service returns)... so I'm inclined to leave it as it is.
So the bottom line is that the code in these plugins is quite a bit clearer and cleaner using Url and Link, and I'm inclined to leave it as it is. Thoughts?
7. Typo, fixed.
Anyway, here's a new patch and interdiff. Also updated issue summary to reflect plugin vs. hook.
Comment #16
vijaycs85Looks great.
Few comments/ideas:
Is this time to introduce help_ui module so that we can load all UI stuff there and leave help module with basic UI and option to provide help?
This(help_ui) module can provide a help page with search field to look for help topics and help sections (document, tour, etc). more like this:
minor: when I read admin/help, I read it like "admin or help" page. Can we say "help index" page or something like that?
this can be just "help"
'hook_help' need to updated as well.
I think this is the first time we have permission in plugin. is this restricting to use user_permission access?
Are we going to handle the route with param in future? or this is how it will be? can we add some more documentation why it doesn't work for our case?
Comment #17
vijaycs85Comment #18
jhodgdonThanks for the review in #16! Regarding your points...
1. The modifications here are fairly straightforward (making the page flexible and listing tours if available); if we get crazy with graphics and stuff (on another issue presumably) then we can think about splitting it up, but I don't see a point to having a Help module with a separate UI module at this point.
2. Updated to make it clearer this is a URL but I think saying "help index page" is confusing, as it is not called that anywhere else. The wording in this section is now parallel to the existing docs in hook_help() above in that same file.
3. I specifically used "hook_help" for this plugin because it is listing the pages provided by hook_help(). I'd prefer to leave it with this ID.
4. The permission annotation is documented on the Annotation class included in the patch.
5. There is no way to handle routes with parameters. I've made another try at updating the docs to make this clearer.
6. There are not many tours that exist currently in core. If you turn on the language/locale modules, you will have a few more showing. See #1921152: META: Start providing tour tips for other core modules., which hasn't been updated in a while.
Anyway, here's a patch with some updated docs...
Comment #19
wim leersWTF, I posted a comprehensive review that I spent 30 minutes on, but it's not here! :( :( :(
Here's a short version with the most crucial points.
user.permissions, notuser, because varying by permissions, not something user-specific.This feels very, very wrong. Hardcoding a layout in a method name is bizarre.
It feels this belongs in a template instead.
We have a standardized solution for this:
\Drupal\Core\Cache\CacheableDependencyInterface.Again
user.permissions.Comment #20
jhodgdonThanks for the review in #20, and what a pain to lose your comment! Ugh. Anyway, I hope the important points are all there.
Regarding your point #2, the fact that it is a 4-column list is hard-coded in the way the method works (it divides up the list into 4 chunks) and in the class added to the render array (layout-column--quarter); that said, it does degrade nicely to a 1 or 2 column list on a phone or other smaller device. This is NOT a change from the existing help page; I only separated out the code that made the 4 columns into a separate method (I needed to call it on each section of the page rather than just once). And I changed the code slightly -- used array_slice() instead of the slightly more convoluted process used in the previous code for dividing the array into 4 parts.
Anyway... since this is how the existing page works, I think fixing that would be a separate issue. There were also two previous issues dealing with these points, which is how it got from the even worse code it used to be to where it is now:
#2408481: Rewrite help.module component's inline with our CSS standards
#2337317: Replace help page layout CSS with reuseable layout classes
By the way, regarding the search feature for help mentioned in the review in #16, there is already an issue related to this at #102254: Add an administrative search feature.
The attached patch/interdiff I think fixes the other points in #20. Much better to use that interface, excellent idea! I decided that it merited making a base class now (as was suggested a few reviews ago by larowlan), since I think most implementers will have now 2-3 methods they will want to leave empty.
Comment #21
jhodgdonwhoops, uploaded the patch twice instead of the interdiff. :(
Comment #22
jhodgdonDang, can't do anything right today. I also forgot to add the new plugin base class into that patch. It's also not in the interdiff file.
Comment #25
jhodgdonOne more patch - cleaning up some extraneous use statements, at least I hope they are extraneous. The tests are passing, and I think/hope I've addressed most/all of the review comments.
So... I really appreciate all of the reviews so far... Anyone have more suggestions for things that should be improved in this patch? I would like to get this patch finalized soon if possible so that I can work on #2351991: Add a config entity for a configurable, topic-based help system and hopefully make the 8.1 deadline at the end of the month (that issue is postponed on this one).
Comment #26
andypostLooks great!
The main question about performance cos new plugins and hook.
one more alter for each help block?
maybe better use #access for render then skip content
nit, extra line
HelpSectionPluginBaselooks better nameis not used?!
Comment #27
jhodgdonThanks for the review in #26 @andypost!
1. All plugin managers have an "alter" hook to let modules alter the list of plugins, so I put one in here. It is possible that not all of the alter hooks we have are documented, but all/most plugin managers have that alter hook invoke in them... This is pretty much a standard plugin manager construct method (in HelpSectionManager class):
So this alter is being invoked once when you go to the admin/help page, when someone asks for the HelpSectionManager to list all the help page section plugins. It will only actually get invoked if the help section plugins list cache data has been invalidated or is missing. It is not affecting the help block, so it is not happening on every page, just when you go to admin/help, if someone has installed/uninstalled a module or cleared the cache or something.
2. The page is being cached based on user permissions already. So it seems to be a waste of time to calculate something that will never be displayed? I think it's better just to skip it if the user cannot see it anyway. (You brought up several performance issues -- why add to the problem?)
3. Fixed. Thanks!
4. Good idea. Fixed. Thanks! [Note that the interdiff doesn't show that the HelpSectionBase class file name changed, due to me not being great at creating interdiff files, but it is changed in the patch.]
5. Hm. That HookHelpSection plugin extends PluginBase, which uses StringTranslationTrait. So it seemed like a good idea to inject the string translation service, in case any methods on PluginBase used t()? Not sure. Is this unnecessary?
6. (implied) I'll get a change record drafted shortly and update the issue.
So... somewhat flawed interdiff file and new patch attached. I haven't tested the patch but it should be OK hopefully.
Comment #28
jhodgdonChange notice drafted:
https://www.drupal.org/node/2669966
Comment #29
larowlanOnly one thing left here from my point of view
I still think this should default to 'access administration pages' and not ''. Then implementations can omit that key rather than pass ''
This is nice
Comment #30
jhodgdonRegarding #29, they can already omit the permission key. In fact, if you look at HookHelpSection, it does that:
which, according to the docs in the annotation, means:
To clarify, the admin/help page is already checked for the 'access administration pages' permission due to the help.routing.yml entry:
So if a HelpSection plugin has no permission beyond that, it just leaves 'permission' out of the annotation, and no further permission check is performed. There is no reason to default the permission in the annotation to 'access administration pages', and check that same permission again in the Controller when it decides whether to build each section. Right?
Comment #31
larowlanThanks @jhodgdon - works for me
I manually tested this and encountered #16.6 - I think we should skip that section if there are no tour links.
The reason we get 'view edit page' with nothing else (no link) is because that tour is for editing a view - and we obviously don't have the context of a view (hence your try/catch block). In those instances I think we should just skip it, not output plain text - thoughts?
Sample with extra modules enabled - looks great
But, again here I think the 'editing languages' one should go.
Comment #32
jhodgdonActually, I don't agree about the tours and plain text vs. links. I put that in because I really do think it's useful to know that a tour exists for the "editing languages" or "editing a view" pages, even though you cannot go there directly with a link. The description text for the section explains this with:
Eventually it would be nice if #1921152: META: Start providing tour tips for other core modules. got done so we had a sensible list of tours on the page in the first place, so that the section wouldn't be so empty...
And as a note, the Help controller does have some code that outputs some text if the list of topics is empty, but again I think having the section there is useful.
Thoughts?
Comment #33
jhodgdonRE #31/#32, we should let the Tour module maintainer decide this.
Comment #34
jhodgdonand/or Usability team.
Comment #35
andypostPermission to display section or content within?
If we gonna reuse help for customer's website page then we need control how to hide some sections for roles at lest
Comment #36
jhodgdonThe permission in the plugin annotation is the permission to display the entire section. Any module that provides a section plugin can define a permission like "View xyz type of help" and then that can be given to a role. A user without that permission won't see the section at all. For instance, if you don't have the tour viewing permission, you won't see the Tours section, because the Tour plugin has that permission in its annotation.
Then each individual plugin may also check permission or do whatever it wants when making the list of topic links (so it could hide certain links for certain permissions etc.). It can also put whatever it wants into the cache metadata for the section, using the methods on the plugin. For instance, the Tour module adds the entity list cache tag for Tour config entities for its section. The controller adds the user.permission cache tag to the page as a whole, so that if a user with different permissions views the page, it will be rebuilt for them.
And then presumably the standard Drupal route permission system will be used when someone clicks on a link to view a topic or see a tour or whatever. So for instance if the Tours section lists a tour for adding a language, and someone who doesn't have administer languages permission clicks on the link, they get a 403.
On the other hand, the core hook_help() pages don't do much about permissions -- this system was in place prior to this patch. You just need the standard "access administration pages" permission to view admin/help or any of the hook_help() module pages like admin/help/node. This is true whether or not you have any admin permissions for that particular module; for instance, you can read about how to add content on the admin/help/node page, even if you have no add content permissions. But that has been true forever in Core. This patch doesn't change it in any way.
So... I think the system is sufficiently flexible for whatever use case, and the tests in this patch do test the permissions stuff...
Comment #37
wim leers#32: I'd love to do it for the Text Editor and Quick Edit modules. But I've been told I should postpone working on tours because a set of rules/style guide/something like that was being created for tours first?
#2665672: hook_help() should allow render arrays for return values landed, patch in #27 doesn't apply anymore.
admin/help->/admin/helpI stand by #19.2.
I think this should all be taken care of in
help-section.html.twig.I think this needs to get sign-off from Cottser for that.
The
getCacheMaxAge()implementation is wrong. Once it's fixed, it'll be identical touse \Drupal\Core\Cache\UnchangingCacheableDependencyTrait. So I suggest doing that instead.Nit: 80 cols.
Yay :)
Just one detail: s/tour entity/tour entity type/.
This one is unnecessary.
Why not omit the part between parentheses in case it changes some day?
Would
$toursinstead of$entitiesbe better?This is a big cacheability bug: the cacheability metadata for being able to access a URL is simply thrown away. It means we may divulge links to routes the current user cannot access.
listTopics()is allowed to return a list of links or a list of render arrays. But that's not good enough; we need to be able to specify the cacheability of the list of topics itself, which then can describe why some links are absent/presentComment #38
jhodgdonThanks Wim! First, here's a reroll to merge with the changes from the other issue. And regarding creating tours, see that meta issue linked in #32.
I'll address/fix the rest in a separate patch because I have enough problems with interdiffs as it is without combining them with rerolls. ;)
Comment #39
wim leersIt's simpler to review too then. Simplicity++ :)
Comment #40
jhodgdonSo, regarding the review in #37 -
1. Fixed in two places in this file, plus a few other files. Some rewrapping required.
2. I do agree with you on this... I talked to joelpittet about this, and got it working. There's a new template for help sections for Classy (to override the default one in modules/help and Stable theme), and now HelpController does not output markup itself. Woot! Definitely an improvement.
3. Excellent, thanks for pointing that out!
4. I don't see any comments going over 80-character lines in this patch... ???
5. Fixed.
6. Got rid of the local variable entirely -- combined it with the next line of code.
7. Good idea, fixed.
8. Sure, also changed $entity to $tour.
9. Hm. So. In TourHelpSection, getCacheContexts() does this:
I thought that this would take care of making sure that the page cache is not shared for people with different permissions to access routes?
If not, do you have a suggestion for what I need to do instead? Thanks!
Anyway. Here's a new patch and an interdiff. Tested manually and works fine; let's see if the bot agrees.
Note: The interdiff does not include the new core/themes/classy/templates/misc/help-section.html.twig template file, because I am lousy at creating interdiffs that contain both new files and changes to existing files. Sorry about that!
Comment #41
dimaro commentedUpdate to run the tests.
Comment #42
jhodgdonWhoops. Forgot that. Thanks!
Comment #43
jhodgdonDoh. Realized that in Stable/core template, since we are not outputting an item-list of links any more, I needed to add something to the template. Verified manually that the page works in Stark theme as well. It just makes a simple UL list of the topics.
Comment #44
wim leersYes, that takes care of varying by permissions.
But this is doing something else entirely: this is showing URLs that the user may not be able to access. Being forbidden access to a certain URL can depend on anything, it could be per user, it could be time-based, it could depend on some configuration, etc. It is that cacheability metadata that is missing. Which means that if
/admin/helpeventually is cached (which is unlikely because not very useful), or if something else starts callingTourHelpSection(which is more likely), then the cacheability metadata will be incomplete.We've solved such problems in the past by introducing a value object that contains the list of things and then also contains cacheability metadata for the overall list. An example is
\Drupal\Core\Breadcrumb\Breadcrumb, which as it happens also contains a list ofLinks.But perhaps it's okay to ignore this because this is something that is not really ever worth caching. However, if we conclude that, then we'd need to specify
max-age=0.(I was first tempted to say "well there's no harm in disclosing a certain URL to people who clearly already are site builders, the worst thing that can happen is a 403". But if this were to be cached, then it'd be vary by
user.permissions, which means that if user A cannot see link 3, but user B can, and users A and B have the same roles/permissions, then the output for both will depend on which user first accesses/admin/help.)Assigning to catch for feedback for this aspect.
Other than that pesky detail, this patch now looks splendid I think :)
Comment #45
wim leersTagging for #37.9 + #40.9 + #44.9.
Comment #46
jhodgdonHm. I see what you are saying about the cacheability -- thanks for taking the time to explain it.
So, let's see. The TourHelpSection plugin is trying to make a link to any of the possibly several routes (usually admin pages) that the tour could be displayed on (for instance, a tour could apply to both the Add and Edit page for some config entity, which means it has 2 routes it applies to).
So TourHelpSection does a loop over the routes, where it checks if the route:
a) Can be created (without any special route parameters).
b) Can be accessed by the current user.
If the first route works, it makes that link. If it doesn't, it keeps trying the rest of the routes where that tour can be displayed, until it finds one and makes that link. If none of them works, it just displays the tour title as not a link. So this isn't quite like breadcrumbs, because they have definite URLs at least.
Yeah, I'm inclined to say that the TourHelpSection's list of links should not be cached.
Here's one more patch... I was going to fix the 80-char thing in HelpTest but with the comma, it hits 80 in my editor so I didn't.
Comment #47
tim.plunkettI don't think this should implement ContainerFactoryPluginInterface. That should be up to each implementation.
This is no longer using StringTranslationTrait, so it doesn't need the service injected.
Same here, AFAICS
Comment #48
jhodgdonThanks Tim for the review in #47 -- all good points.
Someone brought up the Translation thing before... I wasn't sure because PluginBase itself uses StringTranslationTrait, which kind of implies it expects the translation service to be included. But if you say it's OK not to do that, ... yeah I would not see anything PluignBase would want to translate. Good! Took that out.
And you are right, we should leave it up to the plugin whether they need to be container-aware or not. I think most would, but you never know, maybe Advanced Help will use this and do some kind of file system listing. :)
So here's yet one more patch... tested manually, still works... Hopefully this is converging to something Good For Drupal 8 (TM).
I just want to say again, I REALLY appreciate all the excellent reviews from everyone! Definitely improving the patch, little by little. ;)
Comment #49
jhodgdonI just made a minor update to the change record for this issue. Also updating the issue summary to add a few points.
Comment #50
jhodgdonSorry, wrong related issue there.
Comment #51
wim leers#46: there is one alternative approach I thought of after having written #44.9 (actually, while trying to sleep :P). That approach is to not let
HelpSectionPluginInterface::listTopics()return a list, but let it return a render array. But that also doesn't really make sense. It also limits the ability to reuse the information.So I think
max-age=0solution you went with in #46 is fine. But the comment that you added is not really accurate: we can actually get that information. You're just choosing not to deal with it, by settingmax-age=0.BUT! Having written this made me think of one more approach that you may find more acceptable: pass a
CacheableMetadataobject intolistTopics(), and let it be altered. This object then contains the cacheability metadata of the list.\Drupal\Core\Menu\MenuLinkTree::buildItems()and\Drupal\Core\Menu\MenuParentFormSelectorInterface::getParentSelectOptions()also use this for example.I was going to RTBC this and fix my own nits. But now I'm going to reroll it twice: first to fix my nits below (points 1–3). And then I'm going to implement the suggested approach I gave in the "BUT!"-paragraph (which also fixes point 4 below). Then you can see what that would look like. That then only leaves point 5 below to be answered by you.
s/user.permission/user.permissions/
99% of the time we call this
$cacheability. Let's do that here too.Needs newline in between.
This information is wrong.
Are you sure you want this to live in Classy? Shouldn't this be the default template, i.e. the one in
core/modules/help/templates? If it were, then it would work for all themes, not just Seven.Comment #52
wim leersTo do what I said in #51, I also had to change
Url::access()andUrl::renderAccess(). Both of those still relied on booleans, they did not supportAccessResultInterfaceobjects yet. Both of those methods were introduced in #2302563: Access check Url objects. We forgot to update them in #2287071: Add cacheability metadata to access checks, probably because that last issue landed so shortly after the first issue, it was simply overlooked.So:
interdiff-relevant-changes.txtcontains the actual changes relevant to this patch. Basically, what I proposed.interdiff-url-changes.txtcontains the changes to\Drupal\Core\Urland its test coverage, basically to fix an oversight. This doesn't break BC.interdiff.txtcontains all changes.Comment #53
wim leersI'd like @catch's feedback on #52.
Comment #54
dawehnerHow is that not a API break?
Header is a bit confusing, because you would think, you could put some HTML there, but its just a title, isn't it?
IMHI we should just use the plugin instance here as well. No reason to deal with the array structure, unless needed.
IMHO we should also add the description/header on here?
Comment #55
wim leers#access_callback: such callbacks return either booleans orAccessResultInterfaceobjects. It now becomes the latter.However, if you want to be 100% safe, we could add a new
public static function renderAccessWithCacheability()method that does this, and maketoRenderArray()use that instead. Then any code that is currently callingrenderAccess()directly would get the exact same result.Comment #56
jhodgdonI like the approach in the latest patches.
So... Regarding #52 - item 5 -- there is a simple template that is in the Stable theme (as well as a core template), which has no classes. Templates with classes belong in Classy, so that is where the 4-column theme template goes. I think Cottser/joelpittet would agree on this being the right thing to do, or at least that is what I thought Joel told me to do in IRC yesterday. Anyway, the help page *does* work in Stark, in the sense that the topics are listed on the page with no stying decisions (such as dividing the page up into exactly 4 columns, which is Classy's choice and should not be part of Stark).
Anyway, a few nitpicks on the latest patch, and 1 substantive comment:
In text, "boolean" should be "Boolean". It is a proper noun.
Also, as you pointed out on one of my earlier patches, in the 3rd line the word "When" can be moved up to the previous line. ;) Funny how we can see this in someone else's patch...
Also, in the 4th line, please do not use "i.e.". It is not punctuated properly and most people (as demonstrated by many many many examples of bad docs that we have laboriously fixed in Core) do not understand what it means. Really, we had some docs cleanup issues that got rid of all of the i.e. and e.g. in Core docs... it was painful to see how bad they were. So, please use "that is". And the punctuation should be:
... is returned; that is, TRUE means...
I agree with dawehner here -- this definitely smells like a BC break.
This is a public static function. Changing its return value cannot be OK. You cannot guarantee me that no one can call this outside the context where you say it doesn't break anything. If we need a function that returns an object then we should define a new function, not break BC on the old one, right?
I do think this is a good addition to the API. Thanks Wim! Although it is a bit weird to have two separate places to get cacheability information, one for the plugin itself and one for the listTopics() method. Is it not possible we could combine them?
We do not normally put & in @param doc lines. Please take that out.
I would not use "accessibility" here. Accessibility to me means "can people who are blind or have mobility limitations use the page", not "do I have access permission to see it". At least, it's definitely ambiguous.
Also, you are assuming that the only reason listTopics() has cacheability metadata is due to access checking, but it's certainly possible for some implementation of this plugin to do something else that requires cacheability, right?
So.... my suggested wording here would be:
Cacheability metadata object; add cacheability information about the returned list of topics to this object.
But agin... I'm not really sure why we need to do the cacheability stuff in two places in this plugin. There's really only 1 use for this plugin -- making a list of topics. Why should the plugin and listTopics() have separate cacheability stuff? Can't we just combine them?
Is the & really necessary here? I think objects are always passed by reference. See
http://php.net/manual/en/language.oop5.references.php
I'm pretty sure we do not need the & here.
Just pointing out that this is the default template implementation (which by necessity is also copied in Stable theme).
It just outputs a UL list of the links, not divided into 4 columns.
So due to your patch for listTopics() just below, shouldn't we be taking this out?
Technically, we are adding more cacheable dependencies than we really need here, because some of the routes will need parameters so will be try/catched out below, but that is probably OK.
And this one is the Classy theme implementation, which is more complex: divides up into 4 columns and adds the column--quarter classes.
And then here's the Stable copy of the Core theme.
Comment #57
jhodgdonAnd... I am now officially handing this over to Wim because I am NOT making changes to the Url object.
I was quite happy with the "don't cache the list of tours" solution. If we need to make this many changes in order to get proper cacheability information for a page we probably aren't going to cache anyway... why are we doing this?
Maybe we should just go back to the patch in #51, address dawenher's feedback in #54, and actually get this finished by the Beta deadline. I would really like to do that... thoughts? I fear that the current approach is not going to make it in at all (BC break and I don't understand the implications of removing it). I would really really really really not be happy if we miss the deadline over something that is probably not necessary.
Comment #58
jhodgdonOK... In case the approach in #57 is acceptable, here is a patch that:
- Starts with #51 [Wim's fixup of a few "nits" from my last patch in #48].
- Fixes the comment in the Tour plugin for the getMaxAge() method mentioned in #51, which wasn't fixed by Wim because he took a different approach in #52.
- Addresses @dawehner's feedback in #54, by adding getHeader() and getDescription() methods to the plugin interface and base class. Also clarifies the annotation docs for 'header'.
- Well, then I decided #54 was right and the annotation/method should be called 'title' instead of 'header', so I went and changed that.
Note that the interdiff is to #51.
After clearing the cache, the Help page still looks the same... so I expect it will pass the tests.
Thoughts? Further suggestions?
Comment #59
jhodgdonAny thoughts on the latest patch/approach? Anything else left to do?
Comment #60
larowlanI agree, changing Url object is out of scope here.
Comment #61
yoroy commentedThe added flexibility to also show contrib help is good and useful. Listing the tours is a nice addition as well.
Comment #62
catchfwiw I think it's fine to go with max_age = 0 for now. It's good to know if we ever wanted to we could add caching later, but this is neither the most expensive nor the most frequently visited of admin pages. Unassigning me for that bit, haven't done a full review here yet so just leaving RTBC.
Comment #63
jhodgdonTwo notes:
- RE #61 - I already have the Configurable Help (sandbox currently) contrib module using this patch to add editable help topic pages to admin/help... I was going to try to get that into Core too but this issue was a prereq and there is not time for 8.1.x. Maybe 8.2.x, since webchick (Product Manager) thinks it is a good idea. We'll see.
- RE #62 - only the Available Tours segment of the page is uncacheable currently. So if you don't have the Tour module enabled, admin/help is cacheable. And we can probably add cacheability to the Tours section later if it becomes important.
Comment #64
wim leers+1. As
interdiff-relevant-changes.txtshows, it could be implemented in a way that maintains BC.This patch looks great now. I just wanted @catch's sign-off on this wrt cacheability.
I have zero remarks left for this patch. Sorry that it took so long, @jhodgdon.
RTBC++
For #52.2's
Url::access()changes — which are indeed out of scope here but are necessary anyway, I opened #2677902: Url::access() doesn't allow cacheability metadata to be bubbled.Comment #65
mile23Reroll of #58.
Comment #66
xjm@jhodgdon and I discussed whether this issue could still go into 8.1.x, and I think this is definitely worth considering as a beta phase change. I didn't review the patch in detail, but the overall changes seem a possible beta target for me. Let's discuss committing to 8.1.x as well when we review this for commit.
Comment #67
jhodgdon#65 appears to be exactly the same file as #58. diff finds no differences. ???
Comment #68
mile23Oooookay so now the patch in #58 applies where it didn't 5 minutes ago.
RTBC +1, given testbot green.
Comment #69
wim leers+1 for the analysis in #66. I consider this very low risk.
Tagging , akin to .
Comment #70
star-szrMinor coding standards and Twig docs stuff, leaving at RTBC:
Minor: the return and closing square bracket line are indented by one space, should be two.
Minor: These should just say "Theme override for…"
Minor: Our current policy is that @ingroup themeable should only be on the core templates although a handful of templates in Bartik, Classy, and Seven violate this.
Comment #71
xjm(We're not going to do any beta target triage process, so removing the tag. Committers are evaluating issues in the RTBC queue as normal. Thanks!)
Comment #72
wim leersSorry!
Comment #73
xjmThe committers all agreed to commit this to 8.1.x as well as 8.2.x. If the bot comes along to move it to 8.2.x before we get the chance, go ahead and move it back. :) Thanks!
Comment #74
xjmI added the screenshots to the summary (are they current)?
Comment #76
xjmAs per #74.
Comment #77
star-szrHere is #70.
Comment #78
jhodgdonThanks @cottser for #77. Thanks @xjm for #76. And yes, the screen shots are current.
Comment #79
alexpottThis looks incredibly useful. It would be great to encourage more tours in core.
There is no test coverage of this. We should create a test help plugin that has nothing to display.
Comment #80
jhodgdonRE tours in Core, see this meta/strategy issue, which is stalled and needs to be restarted:
#1921152: META: Start providing tour tips for other core modules.
RE testing "nothing in section", good idea. I'll take care of it shortly.
Comment #81
jhodgdonHere's a new patch with the requested test for the empty section.
After looking at the screen shot of the verbose output of the test, I decided that the "nothing in the section" sentence should end with . -- it looked really weird without that. So, added that to HelpController, added a bit to HelpTest, and added a test plugin to the existing help_page_test test module.
Comment #82
jhodgdonReviews welcome. :)
Comment #83
larowlanNew test looks good
Comment #84
alexpottCommitted 1ac67bb and pushed to 8.1.x and 8.2.x. Thanks!
Comment #87
jhodgdonWOOT! Thanks all!!
Comment #88
wim leersPublished the CR: https://www.drupal.org/node/2669966.
Comment #89
xjmComment #90
jhodgdonThanks @Wim Leers. I was just going to check to see if the change record had been published. :)