Problem/Motivation
The help system needs search capability.
Proposed resolution
Probably, using the existing Drupal core Search module to build the search functionality would make the most sense. But build it in such a way that Search API contrib module, or other search modules, could easily grab the help text to index and make it searchable.
A note about caching:
- We're really only concerned here about caching for the search results page.
- The Search module already puts cache tags in based on the search index (which we have a test assert for). So that means whenever the search index changes (i.e. for whatever reason we have decided to add, delete, or update an entry for a help item), the search results page cache is invalidated. That should be sufficient to take into account the idea that if the list of (and contents of) help topics that are searchable changes for any reason, we should invalidate the search results page (because you would get different search results potentially).
- We do also want to let each item in the results, as it renders itself, to declare cache stuff (this is also in the patch/test).
- We also need a cache context for user permissions, because which items can be searched depends on user permissions.
- We should also restrict the search to only look for topics in the current page language, and add a context for the interface language.
Remaining tasks
- [Done] Implement search indexing and searching, using the core Search module. The latest patch (#54) has this working. Here is what the search results look like:
- [Done] Once it's finalized, we'll need to update its hook_help, and also add a Help Topic for it. NOTE: The help topic could use some links to help topics that don't yet exist about creating search pages and block placement. We'll have to do those later when we clean everything up in the topics on #2687107: Reorganize topics into sensible outline, and/or write more topics so I'll add a note to that issue.
- [Done] Add a search box to the main Help page. This depended on #3067943: Add capability for search blocks on non-default search and looks like this:
- [Not doing at least now] Possibly add search block to other admin pages?
- [Not doing at least now] We discussed having a keyword functionality -- a field added to the HelpTopic plugin that would define keywords that could be emphasized in the search, but decided that would not be part of this patch.
- [Done] Write a test. Things to test:
- [Done] If you enable Seven theme, you get the search block present on the help page.
- [Done] Search works for some keywords. Probably best to use the test module HelpSection plugin for that.
- [Done] Search permissions -- both the overall permission and the one that is used for a given help section. The existing help tests verify this with a section that has permissions, so we could make that HelpSection plugin searchable for further testing.
- [Done] Internationalization of search -- again with that test plugin... the HelpSection plugins have control over how to render items for indexing/results in a given language, so it would not be too hard to fake the internationalization bit in the test.
- [Done] Also add tests in the Help Topics module that verify that searching works with Help Topics in particular, and the internationalization of search works for Help Topics. But best to have the main tests independent of Help Topics I think?
- [Done] Cache tags/contexts/etc.
User interface changes
Help topics provided by the Help Topics module will be searchable. There is a new block on the main Help page that lets you search help.
API changes
No changes to existing APIs.
New API: HelpSection plugins that also implement the new interface \Drupal\help_topics\SearchableHelpInterface will make their topics searchable. So far, only the HelpTopicsSection in the help_topics module implements this interface.
Data model changes
A new database table help_search_items is added to keep track of the IDs of help topics that are searchable.
Steps to test
- Apply latest patch
- Enable
help_topics
module - Go to admin/config/search/pages and click "Re-index site"
- Run the cron
- Go to /admin/help and search for a keyword. e.g. shortcuts and make sure there is at least a result
Release notes snippet
We have added a new search type (that is, a new @SearchPlugin plugin) to Core, to go along with the Node and User search types that already existed. This search type is for searching help, and its plugin is called \Drupal\help_topics\Plugin\Search\HelpSearch (it is in the experimental Help Topics module). Using this type of search, administrators can define search pages that will provide help search to site users with permission to view help.
Modules that provide @HelpSection plugins (which define sections for the admin/help page) can make their help text searchable by implementing a new interface \Drupal\help_topics\SearchableHelpInterface on their HelpSection class, which requires implementing two methods: listSearchableTopics() to list the IDs of help topics that should be searchable, and renderTopicForSearch(), which renders a single ID for either help indexing or help search results. As an example, see the \Drupal\help_topics\Plugin\HelpSection\HelpTopicsSection plugin, which is currently the only HelpSection plugin that implements this interface. (So, currently if you search help, you are only searching Help Topics managed by the Help Topics experimental module.)
Comment | File | Size | Author |
---|---|---|---|
#213 | 2664830-3-213.patch | 62.25 KB | alexpott |
#213 | 207-213-interdiff.txt | 2.36 KB | alexpott |
#187 | interdiff.txt | 10.18 KB | andypost |
#185 | interdiff.txt | 4.11 KB | jhodgdon |
#170 | interdiff.txt | 8.72 KB | jhodgdon |
Comments
Comment #2
gnugetI want to work on this but not sure to understand what do you mean with "index"
Also, I think Search API already provide a way to search for any custom entity and it is already stable, should we let seach api to handle the search instead to create our own?
https://www.drupal.org/project/search_api
Comment #3
jhodgdonThe Search API project is not an OK plan for this, if we are going to put this in Core. We would instead need to use the generic Drupal core "Search" module.
So there are two possible strategies here:
a) Search -- add the text of topic pages to the Drupal Core search index, and then when someone does a help search, that index is used to find if there are matching pages.
b) Index -- each help topic would define its own entries for the help index (kind of like keywords). For instance, a topic about how to add fields to a content type might say that in the Help index, it wants to have entries for
Content type
Field
etc.
Then the Configurable Help module would build an index by collecting all the help topics' index entries and combining them alphabetically, and it would provide a page that would list the index entries, similar to the index you would find in the back of a programming book.
So... These two strategies are a bit different. With the Search strategy, each word in a topic would be indexed. With the Index strategy, topic authors would manually define what the important keywords were for indexing.
Comment #4
gnugetThanks for the explanation.
I haven't experience with any of the two approaches, so I couldn't say which one is the most ideal for this project.
I can help building them though.
Let me know if you decide which one is the most ideal for this.
Thanks!
(I had a very long month, sorry for disappear!)
Comment #5
jhodgdonIt seems to me that probably the search approach is probably best for this project.
The way the core Search API works in Drupal 8 is this... To tell the core Search module that you have some content to search, you define a config entity plugin of type SearchPage:
https://api.drupal.org/api/drupal/core%21modules%21search%21src%21Entity...
There's documentation on how to do D8 plugins here:
https://api.drupal.org/api/drupal/core%21core.api.php/group/plugin_api/8...
See the "Creating a plugin of an existing type" documentation. There are also two examples in Core. The most relevant is probably:
https://api.drupal.org/api/drupal/core%21modules%21node%21src%21Plugin%2...
You'll probably want to extend this class
https://api.drupal.org/api/drupal/core%21modules%21search%21src%21Plugin...
And you'll need to implement this interface, so that the help content can be indexed:
https://api.drupal.org/api/drupal/core%21modules%21search%21src%21Plugin...
That's actually basically it, except that it would probably be helpful to add a link somewhere on the Help page to the search page.
Let me know if you need more advice. I was heavily involved in setting up the D8 Search API, and of course I also wrote this module, so I can most likely answer any questions that come up. Thanks!
Comment #6
jhodgdonFrom discussions on #2920309: Add experimental module for Help Topics and #2592487: [meta] Help system overhaul, it is looking like what we really want is a search system. So, I'm changing the title here. Also it is higher priority than just "Normal".
Editing the issue summary also.
Comment #7
jhodgdonI'm going to take this on... Even though it doesn't need to be done before we can get #2920309: Add experimental module for Help Topics into Core. There's not much else waiting to be done. :)
Comment #8
jhodgdonI'm realizing that this should probably be a separate module:
- To use the core Search module's index/search capability (which seems like a good idea!), we would need to provide numeric IDs for each topic/page for the index. So, we'd need to have a database table to save a correspondence between a numeric ID and the config ID for the topic.
- It seems like we should make both the hook_help() module overviews and the configurable help topics searchable, plus (in principle) any other topics provided by other modules on the main Help page (admin/help).
- If we're doing that, we probably need either a set of hooks or a plugin or something that will allow each module to tell the search system about its topics.
- All of that just seems to say to put it in a separate module "Help Search" that depends on Search and Help, and then let each provider of help topics (including Config Help) implement the plugin/hook(s) so that if this help search module is enabled, searching will be there.
Anyway... I don't think I'm going to continue to work on this right now (seems better to wait until Config Help is in Core first), but I'm attaching the SearchPage plugin I started writing so I don't lose that work.
Comment #9
jhodgdonMoving this to Core queue as part of #3027054: Help Topics module roadmap: the path to beta and stable
Comment #10
geek-merlin#3048650: Add "Relevant help topics" block (or re-purpose help block) would benefit from a link index. I don't know though if core search provides this.
Comment #11
jhodgdonI am not sure what you mean by a link index?
Comment #12
geek-merlinAn index for all the links on a help topic page. See the related issue. (Think "What links here" block on some sites.)
Comment #13
jhodgdonIf you mark topic A as related to B, by using the 'related' meta-tag in the Twig file, then the link between A and B is automatically bi-directional. The Help Topics module takes care of that.
But the search module doesn't create an index of links.
Comment #14
geek-merlinMaybe the screenshot on the Help Topics Backlinks page clarifies: Think about a backlink from the "Basic site settings" page to the "Configuring basic site settings" help page.
And yes, it'd need a different plugin than full text search.
Comment #15
jhodgdonI'm going to get back to working on this now that Help Topics are in Core.
Comment #16
vacho CreditAttribution: vacho at Skilld commentedHi here. it is an interesting and very useful feature for Drupal users. Congrats and thanks a lot @jhodgdon for your contributions.
Some aspects that I can recommend(IMHO):
Comment #17
jhodgdonHi!
1. There is no component in the issue queue for help_topics, because we have plans to eventually merge help_topics into help when it gets stable. Check this issue for more information about the plans. #3027054: Help Topics module roadmap: the path to beta and stable
2. This issue is currently assigned to me and I plan to work on it next week. But yes, I will need to render the topic Twig templates and index the resulting HTML content. The patch here was from before. And no, it's not really related to that search result Twig template.
If you're looking for something to help with, please check #3027054: Help Topics module roadmap: the path to beta and stable for an issue that is not yet assigned to anyone. Thanks for your interest!
Comment #18
jhodgdonHere's a preliminary patch. It's totally untested, and not finished, but uploading here as a start. Things to do:
a) Write the findResults() method in the HelpSearch class (currently empty, see NodeSearch for example of how it should work).
b) Implement HelpSearchInterface in at least one HelpSection plugin (presumably the one in the Help Topics module).
c) Add configuration for a help search page, and put a search form at top of admin/help.
d) Does it need its own permission? I don't think so. There are already permissions for viewing help; presumably if you have this module enabled that is good enough to know you want people who can see help to be able to search it?
e) Test manually and get it working
f) Write a test for it
g) Write a help topic and update the hook_help for this new module based on how it actually works, once it's working. A rough idea is in the hook_help now but it may need revising.
A few notes on how it is supposed to work:
1. Modules that want to list their help on admin/help already create HelpSection plugins to tell the Help module about their topics.
2. Now they can also make that same plugin implement HelpSearchInterface. There are 3 methods to implement, which list and render topics.
3. If they do that and the Search, Help, and Help Search modules are installed, their help will be searchable. Site builders will need to create a "search page" (one will be provided by default) for searching help, and put a block on admin/help or wherever for searching help. (Or maybe this module will add this to the page already? that would be better.) Also they'll need to run cron to index stuff.
Comment #19
scott_euser CreditAttribution: scott_euser as a volunteer and at Soapbox Communications Ltd commentedJust a bit more progress on this. Fixed a few fatal errors and got the search index running (not sure if the config install is the correct way, but this was how node and user core modules appear to do it).
Comment #20
jhodgdonThanks... I guess? This issue was assigned to me and I'm working on it.
The usual etiquette in such a case would be to wait at least a week to see if someone is still working on an issue, and then ask if you can take it over. In this case, I made the last patch 4 days ago and was planning to get back to it today. Now I don't know if you plan to take it to completion (you didn't assign it to yourself) or if I can get back to working on it. Please clarify! This is a high-priority issue and I have time to work on it today.
A few notes, looking at your interdiff -- which mostly looks good!
a) I am pretty sure we don't normally add @throws to doc blocks, unless that method explicitly throws an exception itself, which these ones don't. We don't document when a method is simply not handling exceptions that are thrown by things it calls. (Check examples in Core to confirm this, but I'm pretty sure.) If we did need a @throws, it would need an explanation as to why/when, I think? It's a pretty generic exception title. See
https://www.drupal.org/docs/develop/standards/api-documentation-and-comm...
b) Converting simple queries to using select() is not really ideal. See
https://www.drupal.org/docs/8/api/database-api/static-queries
https://www.drupal.org/docs/8/api/database-api/dynamic-queries/introduct...
The queries in my patch that were ideal were copy/pasted nearly verbatim from
https://api.drupal.org/api/drupal/core%21modules%21node%21src%21Plugin%2...
so some of them should probably be restored.
c) You asked about config/install -- yes, that is correct!
Anyway, let me know if you're planning to take this to completion, and if so, assign it to yourself. Thanks!
Comment #21
jhodgdonI'm going to work on something else today... Anyway let me know if you plan to continue or if you want me to get back to this issue. Thanks!
Comment #22
scott_euser CreditAttribution: scott_euser as a volunteer and at Soapbox Communications Ltd commentedSorry I did not see it, was just trying to help! Feel free to ignore.
Comment #23
scott_euser CreditAttribution: scott_euser as a volunteer and at Soapbox Communications Ltd commentedIf you do decide not to just discard the patch, re the database queries static vs dynamic, I had changed them because they were causing my fatal errors when the search index tried to install the config, but for no reason other than that.
If you do want any help on this in the future let me know. I will be away next week but could help the following week before/after work.
Again very sorry for not seeing the assignment! Really did not intend to get in your way.
Comment #24
jhodgdonNo worries, and I definitely don't want to discard the patch (which was great work)! I was mildly annoyed (since I had planned to work on this today), but I'm happy to have someone so enthusiastic about contributing to this effort, so I'm over it. Feel free to take this on now -- I have plenty of other things to do. :)
Good explanation re dynamic queries. I personally prefer them anyway, it's just that I've heard we aren't supposed to use them unless we need them. But let's leave them in, and please go ahead and keep working on this issue. :)
Comment #25
scott_euser CreditAttribution: scott_euser as a volunteer and at Soapbox Communications Ltd commentedOkay thanks for understanding! I'll pick this up when I am back then if nobody else is working on it (I'll check this time haha!)
Comment #26
jhodgdonAh, I missed that in your last comment. If you're going to be away for a week, then I think I'll go ahead and make the next patch, since the other issue I was working on turned out to be pretty trivial. I am working on this today/now for sure, and then I'll unassign if I'm unavailable for a few days, and you can work on it again.
Sorry for being annoyed. It is never a great idea to get annoyed when people make good code contributions. :)
Comment #27
scott_euser CreditAttribution: scott_euser as a volunteer and at Soapbox Communications Ltd commentedWas justified :) Sounds good! I've got a reminder set to look at this ticket a week from Monday whatever the status is, even if to test or add tests.
Comment #28
jhodgdonOK, here's the next patch!
Things I did:
a) Removed the @throws from the doc blocks (as discussed above). We definitely can't have inheritdoc + extra stuff in that one doc block (that doesn't work at all!), and I don't think we need them in the other doc blocks.
b) Changed one if() statement -- if we check that we implement the HelpSearch interface, then we don't also need to check that the methods exist (PHP will complain if you say you implement an interface but miss methods).
c) Implemented HelpSearchInterface for help topics, so they will get indexed. There are some @todo comments in HelpTopicSection about how to deal with languages... I am not sure how to do that.
d) Worked on the code until the indexing actually worked, and I could see that the help topics had their words in the search index.
Sorry, 2 interdiff files (in reviewing the patch, I found 1 more thing to change).
Still to do:
1. Write the findResults() method in the HelpSearch class (currently empty, see NodeSearch for example of how it should work). This will actually let the search part work.
2. Put a search form at top of admin/help.
3. Test searching manually and get it working
4. Write a test for it
5. Write a help topic and update the hook_help for this new module based on how it actually works, once it's working. A rough idea is in the hook_help now but it may need revising.
Comment #29
jhodgdonWooot!
Here's a patch that gets search actually working! Setting this to Needs Review for the first time.
Still to do:
1. Fix the @todo items in the patch, mostly having to do with caching and translation.
2. Put a search form at top of admin/help (currently you have to go to search/help in order to search).
3. Write a test for it.
4. Write a help topic and update the hook_help for this new module based on how it actually works, once it's working. A rough idea is in the hook_help now but it may need revising.
Comment #30
jhodgdonWell, here's a problem... The URL for searching help is configurable (that is how the core Search module's Search Pages work). Also, the Search module only provides a block for using the default type of search that is configured on the site. So... We don't really have a way to set up a block with a search form in it for help, or to add a form that will search help from admin/search, because someone could go to the Search Settings page and edit the HelpSearch page that is provided in this patch to change the URL. Or even delete it.
I think the only way around this would be to make it so that the Search module provides a more configurable block, which allows you to specify which search page you want the search to go to. We'll probably need to spin this off to a different issue... unless we can think of another way to do it.
Anyway, updating the issue summary, and I'm probably done working on this for the moment.
Comment #32
jhodgdonThe test errors seem to be:
- We forgot to add new module help_search to the core/composer.json file
- The dependencies might be incorrect, not sure about that? Maybe the composer fix will fix that other error
- There is something wrong with the search page config [although I can't figure out what?]
- 2 coding standards messages
So... let's try this...
Comment #34
jhodgdonHm. That fixed one test fail, but the module install/uninstall test is still failing, as is the config test. I am going to try moving the config to the config/optional directory instead of config/install and see if that helps. Interdiff is difficult, but all I did was move that one file to a new location.
Comment #35
jhodgdonOh, duh! We need a config schema file. Adding that, and I'll put the config item back where it was.
Comment #37
jhodgdonHm, there was a test failure that looks unrelated. Hitting retest...
Comment #38
jhodgdonHere's the issue I created for the unrelated test fail, if anyone is interested (nothing to do with this patch):
#3067768: Random test fail in ManageGitIgnoreTest
So, I talked to @alexpott and @pwolanin (who is the Search module maintainer) in Slack today, and they said we should spin off a new issue to deal with the search block stuff. I created:
#3067943: Add capability for search blocks on non-default search
We can work on the To Dos here in the meantime, but we will not be able to complete this issue until that one is finished. I'll add it to the roadmap.
Comment #39
jhodgdonSo, the @todo items in this patch. There are basically 3:
a) Cache tag stuff for the search page. Look at NodeSearch and do something similar.
b) Permissions. There are notes in that @todo on how to do that.
c) Language stuff when indexing and making snippets for search results... Thoughts:
- The titles of the HelpTopic topics are detected in HelpTopicDiscovery, and shoved into a TranslatableMarkup object (without the language being set).
- The body of HelpTopic topics is wrapped in
{% trans %}
in Twig (no language being set there).- To render these into a desired language, it looks like we need to set the default language on the language.default service, and then all rendering/translation will happen in that language.
So, I think I'll work on (b) and (c) today. Also, I don't think we need keywords -- that could be a later enhancement -- so removing that from the issue summary.
Comment #40
jhodgdonComment #41
jhodgdonOK, here's a new patch. I've taken care of two of the @todo items in the code (permission checking, and language stuff). One of them involved a change to HelpSearchInterface, as I decided it made more sense to render the item in the HelpSection plugin and not in HelpSearch. Now HelpSearch doesn't need the Renderer object.
The remaining @todo in the code (cache tags) -- I moved it around to 2 places and added some notes on how to do it. Decided to leave that to the next iteration.
Adding some notes about things to test to the issue summary.
Comment #42
scott_euser CreditAttribution: scott_euser as a volunteer and at Soapbox Communications Ltd commentedTrying to review and progress with this a bit now.
Comment #43
scott_euser CreditAttribution: scott_euser as a volunteer and at Soapbox Communications Ltd commentedGreat progress! All seems to be mostly working for me! I tested this out with and without help_topics enabled and tested out modifying permissions to allow other roles to use the search to see how that behaved with only a couple minor issues (below).
I tried to take care of the two TODOs in HelpSearch but I am not sure if I did them right. When I reviewed the cache tags I see we already have `search_index:help_search` cache tag. I was not sure if that was sufficiently like `node_list` in terms of being a general cache tag that is cleared on search (re)indexing, but put it in for now as the cache tags in
HelpSearch::findResults()
and similar approach for viaHelpTopicSection::prepareSearchResult()
.A couple other things I did here that I came across when testing it out:
HelpSearch::updateIndex() "Drupal\Core\Database\DatabaseExceptionWrapper: SQLSTATE[42000]: Syntax error or access violation: 1055 'db.hsi.plugin_type' isn't in GROUP BY: SELECT hsi.sid AS sid, hsi.plugin_type AS plugin_type, hsi.id AS id FROM {help_search_items} hsi LEFT OUTER JOIN {search_dataset} sd ON sd.sid = hsi.sid AND sd.type = :type WHERE (sd.sid IS NULL) GROUP BY hsi.sid LIMIT 100 OFFSET 0; Array ( [:type] => help_search ) in Drupal\help_search\Plugin\Search\HelpSearch->updateIndex() (line 304 of /var/www/html/core/modules/help_search/src/Plugin/Search/HelpSearch.php)."
Comment #44
jhodgdonYeah, some of the To Dos will need to wait for #3067943: Add capability for search blocks on non-default search... interdiff looks good I think. I'm still not sure about the cache tags. Will need to think more on that.
Comment #45
jhodgdonActually, I do have some thoughts about cache tags.
For Nodes (this may not apply to help topics), there are 2 relevant types of cache tags:
a) There's one that says "this is a list of nodes, so cache is invalid whenever any node is added, deleted, updated". That needs to be present on any search results page.
b) There's one that says "this page's content includes data from this particular node, so cache is invalid if this node changes in any way". That needs to be present on any search results page that includes that node.
The Search plugins have the capability to take care of both types of cache tags. For instance, the NodeSearch plugin does this in its constructor:
(that's the type A cache tag), and then when building search results pages, the Search module adds the NodeSearch plugin as a cacheable dependency on the search results page.
And then NodeSearch, when building the snippet for the search results containing a given node, does this:
So... It looks to me as though your patch adds the type A cache tags, but not in the same manner as was done on NodeSearch. And the type B ones are missing. I think it needs more work.
Comment #46
andypostbtw In related #2966607: Invalidating 'node_list' and other broad cache tags early in a transaction severely increases lock wait time and probability of deadlock cache tags may get split
but for topics search not sure how to calculate "hash" of rendered topics (they may change with code deploy - no hooks)
That could help to clear cache on re-indexing of topics
Comment #47
jhodgdonWell, presumably the caches are cleared when code is deployed, right? And we have set up the Help Search (or at least the Help Topic Search) to re-index when the cache is cleared. So... I think we are OK.
Comment #48
jhodgdonSetting to Needs Work for #45 (need to redo the cache tags stuff from previous patch).
Comment #49
andypostBlocker is in #3067943: Add capability for search blocks on non-default search
So remaining tasks
- adopt new block
- fix cache to tags & cover with tests
Probably it needs release manager to approve new module, I gonna check a way to keep it in help topics:
- schema could be created on demand (
ensureTable()
& check that search module enabled)-
HelpSearchInterface
looks wrong because it is not a plugin's responsibility to be searcheable (contrib may need to use search_api so render and other methods could be different)Comment #50
jhodgdonI do think that a provider of help (as evidenced by having a section on admin/help and hence a HelpSection plugin) will need to declare that by implementing basically these methods. They would be just as useful for search_api contrib module as they would for the core Search module, although the return values might be used differently. But additional methods are definitely needed ... For instance, the list of topics to display on admin/help, in the case of Help Topics, is only the "top level" topics, but all topics should be searchable.
I do think it can be rearranged though. We could call it SearchableHelpInterface, put it in the Help module directly, and make it a bit more generic, such as not asking the plugins to prepare the search result, but instead to return just the information about the URL, title, cache tags, and text. Then the HelpSearch plugin could do the excerpt stuff (which uses the search_excerpt function).
Anyway, I'll work on this -- that might not make total sense in this comment but I have it in my head... I was going to make the next patch here anyway, and I have time today and the next few days.
Comment #51
andypostBtw what about "boost" for search results?
For example topics could be ordered, meta data hits surely should give priority to its topics
Comment #52
jhodgdonI think that would be a possible future feature, but I don't think the base functionality requires it.
The base Search module (as implemented here) will automatically give more preference to higher headers, and some other HTML tags. So since we put the title in an H1, that will have highest priority, and headers will have next highest. It works pretty well. Right now we don't have any other relevant metadata, like keywords defined by the topic...
Comment #53
jhodgdonBefore I get too far, I'm uploading some intermediate patches:
1. The patch from #43, rerolled so it applies cleanly.
2. An interdiff/patch from that that only moves some stuff around, so that we don't have a help_search module at all. I put all the help search stuff into help_topics for now, because adding it to the core Help module would be weird (since it doesn't itself have anything that is searchable).
Note that the interdiff is not all that useful or easy to see, because... well I'm not great at making interdiffs I guess. I made two patches and used the interdiff utility, but it didn't recognize that files were moved and instead said "you deleted this one and created this one". Anyway, I didn't really do anything new in this patch, just moved things around, combined the hook_help, and fixed namespaces.
Anyway. This will not work yet. I need to figure out the schema install stuff, plus the other To Dos. Right now the tables will not be installed.
Comment #54
jhodgdonOK!! Here's a new patch:
- I fixed up the cache stuff (I think?)
- I added the search block. I made it only visible on admin/help. Here is what it looks like:
- All the code is now in the help_search module (see previous patch/comment). Database tables are installed/uninstalled as needed when help_search and search are installed/uninstalled.
- I changed the name of the interface to SearchableHelpInterface, and made it more generic. Now it should be sufficient for both core Search and (hypothetically) Search API contrib module, or anything else that wants to index search. There are now only 2 methods: one to list the IDs, and the other to render a given ID into title, body, etc. (with no dependency on the Search module per se in the code there) (and less code duplication than in the old system).
- It works!!
We still need tests. Updating summary, which lists what tests are needed. But first lets see what I screwed up in the existing tests (hopefully not too much).
Another question (see summary): Do we want this search block visible on other admin pages besides the main admin/help page? I kind of think that is sufficient.
Comment #56
andypostLooking on placement of block after related issue I wonder if we can display search form via special help section with weight -100
Comment #57
jhodgdonI don't think that is what we want to do. I put it into the Help region in the theme, with a weight that put it above the regular Help block. That region is all above the main page content. All the help sections would be in Main page content. So, I think it's correct as it is.
I'll take a look at fixing the test errors tomorrow, unless someone else wants to take the next steps. Basically, to get to Beta now on Help Topics, we just need to get this patch passing, and then write a lot of tests (on this issue and 2 others). I'll pick up this one or one of the other ones, doesn't matter too much to me. I should have some time to work on these issues for the next few days. Anyway, if someone else wants to take this one for a while, please assign it to yourself. Otherwise, I'll assign it to myself and work on it next.
Comment #58
jhodgdonI'm going to work on this today: fixing the test results problems (failed tests and 1 coding standards message), and adding tests.
Comment #59
jhodgdonFirst step: this should fix the test fails and coder errors, hopefully.
Comment #60
andypostWhile you on it, consider to use service - I could make it later (and unit test for service logic)
This hooks "content" needs new service to encapsulate
Comment #61
jhodgdonSince I'm not sure what you mean by comment #60, I think I will leave that to you. :) It is probably easier to communicate what you mean in a patch.
Meanwhile, here is a functional test that verifies:
- Search works in English and translated when searching for help topics from the test module
- Search works when searching for the fake help items from the test help section, English and translated.
- Help view permissions are enforced when searching
- Search block is installed automatically when you enable Seven theme, and works
As a note, I added a new HelpSection plugin to the help_topics_test module, for testing purposes. It returns information independent of the Twig/Topic plugin system, so it's simpler for testing. It may be useful for unit testing as well? It's pretty simple.
Updated the issue summary regarding things that are done/working/tested.
Anyway, I'll let you take a go at the service/test you are talking about in #60.
Comment #62
jhodgdonWhoever takes this next -- note there are 3 small coder problems on the last patch test results page. Other than that, tests pass.
One thing that I didn't test was the cache tags, so adding that to the summary. Also see #60.
Comment #63
jhodgdonI'm going to go ahead and do the coder fixes and hopefully the cache test too, if I can get it done in next 2 hours.
Comment #64
jhodgdonHere's a patch that adds a test for cache tags, and fixes the coder errors. Sorry, 2 interdiffs -- I made one and then thought of something else.
The test is failing locally to find the cache tag for the user who is searching. I'm not sure why that is, because in HelpSearch::findResults(), we are doing:
It seems like this should work... but maybe the AccountInterface object is not enough for addCacheableDependency?
Anyway. I'm out of time for today.
Comment #66
jhodgdonI'm going to work on the cache stuff, got some hints from @dpl.
Also I think we want to restrict the search by language -- node search does this. So if you are currently viewing the admin page you searched from in German, you should only see German results. I noticed when looking at some of the HTML debug output in the tests that there were duplicated links, and I am pretty sure that is why. Anyway, assigning back to me... should have a new patch shortly.
Comment #67
jhodgdonHere is my thought on caching (I'll add this to the issue summary too):
- We're really only concerned about when a search results page needs to be invalidated.
- The Search module already puts cache tags in based on the search index (which I have a test assert for already). So that means whenever the search index changes (i.e. for whatever reason we have decided to add, delete, or update an entry for a help item), the search results page cache is invalidated.
- Because of that, I don't think we need to worry about some of the caching stuff from the previous patch, such as making sure that each help section that might declare itself to be searchable has its plugin manager added to cache tags.
- We do also want to let each item in the results, as it renders itself, to declare cache stuff (which we're already doing in the previous patch).
- As per discussion with @dpl in slack, rather than adding a cache tag for the user who is searching, we should instead add a cache context for user.permissions.
- We should also restrict the search by the current language, and add a context for that. (languages:language_interface context)
As a note, I looked into changing the translation test base class in this patch into a trait, but I think it's better as an actual class that can be extended. This will allow the setUp() method to call parent::setUp(), which is a normal pattern, easily recognized... would be messier code to have a trait, alias that method, etc.
Anyway. Here's a new patch. The tests pass now, and I think the caching stuff is all good.
Comment #68
jhodgdonI ran into a problem when trying to get Configurable Help to work (I had this patch installed). In one place we are trying to call search_index_clear() passing in a list of IDs. That is incorrect. Patch/interdiff...
Comment #69
jhodgdonI am very happy to report that since I got #3072353: Update Configurable Help module so it works with Core Help Topics finished, with the above patch on #68, the configurable help topics also appear in search along with the Twig-provided topics. Woot!
So... I think this is about ready for RTBC. I am still not sure what was meant on #60, but as far as I can tell, that is the only To Do left for this patch.
Oh, we need a hook_help update and a new help topic too. Here is a patch for that, and updating issue summary for that being done. Here's a new patch and interdiff.
Comment #70
andypostOn one hand - core can manage this table for us with
hook_schema()
so updates are easy to manageOn other hand if no search module installed is there reason to have this empty table in DB
Other then that it perfectly RTBC
I still not sure that we should manage schema this way instead of hook_schema, otherwise it is ready, if we wanna manage schema inside of service that's what about #60 was about
Comment #71
jhodgdonYeah, I don't know what the right answer is. I thought we should not have that table present unless both the help_topics and search modules are installed. But maybe it is not so terrible to just use hook_schema, and know that it will be empty unless search is installed. Do we have other examples of this situation in Core?
Comment #72
andypostYes, the example is
\Drupal\Core\Menu\MenuTreeStorage::schemaDefinition()
and other could be found withgit grep ensureTableExists
Comment #73
jhodgdonI looked at other implementations of hook_modules_installed and hook_modules_uninstalled in Core and there isn't anyone doing schema installs. But what I don't know is if any modules are declaring schema that only applies of a certain other module is installed.
Comment #74
andypostFound related, that's why I'm confused between hook_schema and "on demand"
Comment #75
jhodgdonRegarding #72, it looks like all the Core examples that have defined an ensureTableExists() function [there should be a trait! They just all defined them separately] are in core/lib/Drupal/Core. Meaning they are outside the control of any module, so they had to take care of their own schema outside of hook_schema(), and only if their class/service/etc. was being used. And they are all doing that "on-demand" schema.
This situation is a bit different, because we do have hook_schema available.
Probably really help_search should be its own module... then we wouldn't have this trouble. The module would just depend on Search and Help and voila.
Comment #76
jhodgdonOK, we discussed this on Slack and decided the best alternative was probably just to use hook_schema() and live with an empty table in the case that the Search module is not enabled. I'm making a new patch that does that.
Comment #77
jhodgdonOK, here's the patch (that was easy). I verified that the functional test HelpTopicsSearchPatch at least still passes, so it should be fine.
Comment #78
andypostDid a bit of clean-up for CS and method should check only for one interface - only its 2 methods used (listSearchableTopics and renderTopicForSearch)
I did not dig tests a lot but why translation removed? Otherwise it RTBC for me
No idea why this part removed, I mean translations import...
Comment #79
jhodgdonIt was put into the HelpTopicTranslationTestBase class, not removed. :)
Comment #80
jhodgdonInterdiff looks fine to me if test bot agrees. Thanks!
Comment #82
andypostInteresting how it passed before
Comment #83
andypostsorted usage
Comment #84
jhodgdonThis is all looking good to me. +1 for RTBC. Thanks!
Comment #85
jhodgdonI added a change record for this; putting the release notes into the issue summary here too.
Comment #86
jhodgdonUgh. Somehow I managed to delete a bunch of files from this issue. They are all still in the file system, but unlinked from the issue. So, I'm re-uploading the files I messed up. I think I must have had a stale form... sorry about that!
Actually I will do them in 2 comments so that the latest patch is alone for testing. Anyway here are the oldest ones first. These are all andypost's patches.
Comment #87
jhodgdonAnd here's the latest one, also from andypost. I'll try to cancel the other two tests that just launched from the previous comment.
Comment #88
pwolanin CreditAttribution: pwolanin at SciShield commentedQuick first look - seems like a bunch of the code is duplicated from node search. Not sure if that's avoidable.
Comment #89
jhodgdonI do not think much is a direct duplicate. Some code was certainly adapted, but the queries and results are different, plus rendering is different. I definitely didn't copy/paste very much without changing it...
What did you see that you thought we could maybe avoid duplicating? Because we could move stuff to a base class... but I don't think there is much if anything.
Comment #90
pwolanin CreditAttribution: pwolanin at SciShield commentedOverall this looks good.
Confused by deleting from the DB in batches of 100 in
protected function removeItemsFromIndex()
Comment #91
jhodgdonI've run into limits with how many placeholders a query can have in the past. This query uses
so each SID that is being deleted will have a placeholder in the query, because we're deleting specific item(s) from the table.
I didn't run into the placeholder limit in this case in my testing, but knowing I've run into it before, my general practice in situations where I'm doing an insert, update, or delete query that uses placeholders that grow as the size of what is being inserted/updated/deleted, is to chunk it. 100 could be overly cautious, but it should prevent problems down the line. I think in most circumstances, there will only be a few items deleted (e.g. because someone removed a module/theme that had a few topics that had been indexed and don't exist any more), so we won't run into the chunking very often.
Comment #92
pwolanin CreditAttribution: pwolanin at SciShield commentedOk, I realized last night that's probably why.
I think this is good to go forward.
Comment #94
jhodgdonHm. Something has changed recently in rendering, and we're getting a strange test error about body not being a valid render key:
Going to hit retest just in case. If it still fails, I will investigate today or tomorrow.
Comment #96
jhodgdonAh. What happened is that on #3072519: Help Topics discovery cannot be decorated easily we added some YAML-based topics and a TestHelpTopicPlugin class. That class's getBody() method should have been returning a render array, but was instead returning as string. So in the test for searching, we were trying to render/index these new topics from the test class, and they wouldn't render (which we never tested in the other patch that added these fake topics -- they were added to test *discovery* but we didn't actually look at the topic pages).
So. Here's a patch that fixes this method on the test class, so that the fake YAML-based topics can now be search indexed in the test. This is a pretty trivial interdiff (4-line change), and I don't think it really affects the earlier RTBC... but I'll let someone else (and the bot) review the interdiff and new patch.
Comment #97
andypostNo need for
#type
in case of markup, otoh it makes be think about to return renderableComment #98
jhodgdonYeah, I usually prefer to put
#type => 'markup'
in. For one thing, on api.drupal.org, it links to the Markup element class. For another thing, for people new to the render element stuff in Drupal, I think it's clearer to always have a #type instead of having this exception.Comment #99
jhodgdonSo... Do you want me to remove the #type -> 'markup', or is this patch OK to get back to RTBC?
Comment #100
jhodgdonI just remembered that the other classes within core/modules/help_topics are listed as @internal for now. Adding that to the docs header of the new classes in this patch. See interdiff. (I copy/pasted the @internal notation from the other classes that already have it. They all have the same typo about "Help Topic" being internal. Oh well. At least they are consistent.)
Since anything under tests/ is automatically internal, I didn't add that notation there.
Comment #102
jhodgdonThat fail was totally unrelated to this patch. Hitting retest. I'll check and see if I need to file a random test fail issue or if it's a rare glitch or already known or what.
Comment #103
jhodgdonThis is the failure issue.
Comment #104
andypostAwesome idea with internal!
jhodgdon Sorry for misread interface
Let's keep it with @type,
\Drupal\help_topics\HelpTopicPluginInterface::getBody()
require render arrayComment #105
larowlanThis is great work - some observations below
and help_topics just paid for itself
(sorry for the cheesy Australian cultural references)
this hasn't been in a tagged release, is internal and in an experimental module, so on that basis, no need for a BC layer ✅
this could be rewritten as
I'm not sure 'screw up' meets the normal standards for non-test code. Suggest 'interfere with' instead?
On the basis that this could cause exceptions that would bubble up to core's Pokemon exception handling (gotta catch em all) in cron without returning the language to the previous value, I think we should wrap this whole block in a
try/catch
with afinally
block that resets the language back to the previous value, thus ensuring that if things go sideways, we still clean up after ourselvesI think we're missing a $context->pop() here, which would yield a new cacheable metadata object that we'd merge with the existing $cache_meta
But I'm not 100% - perhaps ping @WimLeers for a review to make sure we're doing this bit right. edit: I've pinged Wim
this would go into the finally block with the try/catch approach from above
nit: could be instead of could get?
calling array_filter on the permissions could avoid the need for the
if ($permission
could these be fetched in a single query using fetchAssoc outside the foreach for performance sake?
this implies more than one result for a given plugin - is that the case or just the fact that the query is multi-return? We'd only ever be showing in the one language right?
Would it be simplified if we used fetchRow or something?
we limit the query above to a single language (in findResults) so could this be put outside the foreach for performance?
such a shame there's no OO equivalent of these - follow up?
I think this is indexing items that have been indexed before but are stale - could we add a comment to make it clear?
this could be written as
we can use null coalesce operator (??) here
we could add a continue after this line and avoid the else to improve readability.
same here for the outer else
any reason we don't make this column a SERIAL and just let the DB handle sequencing? we can use the return from ->insert() to get the inserted ID.
this can be written as
$sids = (array) $sids
, i.e a cast operationis there a reason we use batches here, the DB should handle bulk deletes fairly easily?
if there is, any reason not to use array_chunk instead, e.g.
such a shame there is no bulk-operation for this - follow-up?
no need for the else here, we return above
I think we should throw \InvalidArgumentException here as that's what's going on
Why is this needed?
If it is, we can use the $this->resetAll() instead of doing it over HTTP
similarly, we can use a while loop here with CronRunTrait and $this->cronRun() - where the while loop checks that items remaining is zero.
choosing 4 cron runs arbitrarily makes the test brittle, using while makes it more robust
nice, hadn't seen this method before 🤓
❤️
😎
this could use
$this->getSession()->getPage()->findAll('named', ['link', $label])
Comment #106
BerdirI'm not Wim, but commenting anyway ;)
Yeah, since you actually collect the cacheable metadata, I think you want to add something like this:$cache_meta->addCacheableDependency($context->pop());
But not 100% sure, also not if there always is a frame on the render context, it does assert that there isn't more than one, but maybe if nothing actually gets rendered..?
Another option would be to call getBody() through a #pre_render() callback, then it takes care of everything for you.
That's a bit like entities are rendered too, $view_builder->view($node) actually just sets up a minimal render array with meta info and a #pre_render callback. In your case, you'd set #pre_render + maybe #plugin_id or so. Then you'd do all inside renderPlain().
Another note, I find the naming a bit confusing.
It's cache_dependency, but stores on CacheableMetadata object, which is not what the documention of the method says. Yes, it could could hold a different object hat implements CacheableDependency but we know the implementation really has multiple things to consider.
Also, $cache_meta doesn't exist once as a variable name in core, although we use many different names for it, often $(something_)cacheability or $cache(able)_metadata. Although CacheableMetadata::createFromObject() uses just $meta. So pretty inconsistent, but lets not add a new version to the mix. Maybe cacheable_metadata for both key and variable?
What I I also find somewhat confusing is how searching and indexing goes through the same method. Does anything actually happen with cacheablity metadata while indexing? Does display-as-search-result actually need to support rendering in a different language? As far as I see, the answer to both is no. findResults() hardcodes the langcode to the current language and then selects it again, updateIndex() just uses title + text. If we'd have two methods, then render-for-output wouldn't need to care about renderPlain() and render contexts, it could maybe just render a return array. And the other way round, indexing wouldn't have to worry about returning cacheable metadata, it could just drop it.
I'm also surprised that different-language indexing like that works. We just change the *default* language? But \Drupal\language\ConfigurableLanguageManager::getCurrentLanguage() statically caches the result of language negotiation?
Comment #107
Wim Leers#106: thanks :D
What you want is
(which is mostly what @Berdir wrote in #106, but that also answers his
question.)This already happens in a few places, for example:
\Drupal\jsonapi\Controller\EntityResource::executeQueryInRenderContext()
\Drupal\rest\EventSubscriber\ResourceResponseSubscriber::renderResponseBody()
\Drupal\views_ui\Form\Ajax\ViewsFormBase::ajaxFormWrapper()
\Drupal\views\Controller\ViewAjaxController::ajaxView()
I didn't dig into the code in detail since @Berdir already did that! 👏
Comment #108
jhodgdonThanks for the reviews and suggestions! Some of the items in the last 3 comments were more "observations" than suggestions for changes. So... Here are some notes... Responding to comment #105 first:
3 / 4 / 5 / 7 / 8 / 9 / 11 / 14 / 15 / 16 / 17 / 18 / 19 / 20 / 23 / 24 / 26 : Made suggested minor improvements to the code/comments. Hopefully changes are clear and what was desired.
6 [will be addressing that later]
10 : I am not sure about this. The code in question is:
And the suggestion is to do this database query outside of the foreach. ...
So... The problem is that I would still (I think?) need to do some kind of a foreach on $found to extract the IDs from the StatementInterface object (it's not really an array), so that I could extract the plugin types and IDs in a single select. Then the code in the foreach would also be a bit harder to understand... So I would replace having 1 foreach loop and let's say about 10 queries, and fairly clear code, with having 2 foreach loops and 1 query with an IN condition in it, and I think harder-to-understand code. Is that really a lot more efficient? I'm not convinced it's worth it. I have left this as it is for now.
12 : Hmm... well the langcode gets to this function inside $item->langcode... anyway I changed the code so we are only getting the language once and saving it in an array, like the code was already doing for plugins around there.
13 / 22 : Yeah, both of these could be follow-ups. The search API has been stable in Core since about 8.0.
21 : I discussed this with pwolanin above in comments 90 and 91. I did replace the code with array_chunk though, and updated the comment to explain why I did it that way.
25 : I tried replacing this line in the test
with using
$this->resetAll()
. However, the test failed on this line because it got a 404 error visiting the page:So, I put it back the way it was, with a comment as to why it is necessary. This is not a crucial thing to debug and figure out the difference between going to that page vs. calling the resetAll() method, I think? If so, it's probably a follow-up? Suffice it to say the test does not pass (at least locally) unless it is done this way.
30. Well, maybe? I am way more familiar with xpath than with this "named" Behat thing. I did not make this change, because I couldn't quickly find documentation on what "named" does, or why putting "link" in there would work.
OK, back to item 6 from comment 105, and comments 106/107:
- I added the context pop thing to the cache thing.
- I changed the name of the key/variable as suggested in #106.
And one more thing from comment #106 -- regarding having one method for both indexing and results:
I started out with two methods, but there were very few differences between the two. I felt that rather than duplicating a *lot* of code, the most sensible thing to do was just to have a single method that HelpSection plugins wanting to make their items searchable would need to implement (see SearchableHelpInterface in this patch) in order to render an item.
You're right that we don't use the cache meta-data when indexing, so that is a bit of extra overhead during cron runs. But the language could be important for some types of help items that could be displayed in search results. For instance, look at what NodeSearch does when it is displaying search results -- you need the language code in order to render the item at that time.
I can split them up, but really I think it's a lot simpler this way. Let me know if you want to insist.
Anyway... Phew! Here's a new patch and interdiff. The tests still pass locally.
Comment #109
jhodgdon3 whitespace coding standards messages from that last test. Apparently coder wants the break statements indented the same as the case statements in a switch. ?!? Anyway. Whatever.
Comment #110
andypostWhen language changed the whole container got rebuild in child site, that's why call
$plugin = HelpSearch::create($this->container, [], 'foo', []);
will fail - this container no longer validComment #111
jhodgdonOK... so what do you think we need to do for this patch?
Comment #112
andypostCan't check right now, surely it needs follow-up to debug and fix
Meantime changes addressing all feedback, just not sure
break
makes sense after thrown exception inTestHelpSection::renderTopicForSearch()
(sorry, trying to review ftom phone)Comment #113
jhodgdonOK. Well regarding #110, the way the test is set up currently, it is working... I guess we could debug it further but it's not all that relevant for the test or this patch?
Regarding breaks, yeah I guess after a return or a throw we do not need a break. I will fix that. Here's a new patch. Hopefully Coder doesn't require break statements.
Comment #114
andypostPatch rework language related tests and fix minor nits
When test needs to visit language related page the
language => $language_object
should be used.As English set default after install I added
$german
where it needed.I replaced
assertLinkExistsOnce()
withassertSearchResultsCount()
because test should count results and then check titles$session->addressMatches()
removed becauseassertSearchResultsCount()
using CSS selector specific to help searchComment #115
andypostand this change is needed to test for case when searching for "foo" in English
Comment #116
jhodgdonI don't agree with this change in TestHelpSection:
We should have different text in English and German so we can verify the search indexing works correctly in both languages, and the correct text is stored in the index and searched for in both languages.
Then I also don't agree with this change:
We need to verify that the correct text is getting into the search index for the correct language. So we should be putting the word "foomm" in for German, and then testing that you can search for "foomm" in German. The test as it is currently written doesn't really verify that the translations are handled correctly in search indexing, because you are searching for "foo" and only verifying that the title on the search result page shows the German title.
I also don't think this is a good practice in the test:
The problem here is, we *currently* have a topic called "Writing good help". But this is a production topic, not a topic from a test module, so if that topic's title or content changes, the test will fail. We should only search for test topic text and test topic results. Hm... Actually this whole thing looks wrong? Why is "Writing good help" coming up in a search for ABC at all???
Comment #117
andypost@jhodgdon I also wondered to see "writing good help" when started to check browser's output for en/de URLs
The reason to change foomm to foo in text was to make sure that same text in en/de is not mixed in results page (same text in body is indexed in different languages and returning only in requested language).
Probably it needs to make texts more unique to have no collision with existing topics... But have no idea how to prevent it in future when more topics will be added to core.
Comment #118
jhodgdonProbably using nonsense words as the search terms and the title/body text is the best way to make sure that we do not get real topics in the results.
So it seems like we need to verify:
a) When we search in English for a word that is in the English text, we get the correct English results.
b) When we search in English for a word that is in the German but not English text, we get no results.
c) When we search in English for a word that is in both English and German texts, we get only English results. It would be best to search for a word that is in one topic in English and in a different topic in German, and verify we only get the right topic, and it is in English.
d) Same as above a-c but with English/German reversed.
e) Searching works both with Help Topics and with the test fake Help Section plugin.
So... What we currently have for topics in the test module + current patch is:
1. A topic called "ABC Help Test Module" with body (partial) saying "Test translation", with fake German translation of these two strings as "ABC-Hilfetestmodul" and "Übersetzung testen.". This topic is in tests/modules/help_topics_test/help_topics, and the translation comes from HelpTopicTranslatedTestBase [we could add more translations to that test pretty easily].
2. Two other topics "Additional topic" and "Linked topic", plus the rest of the "ABC Help Test Module" that do not have translations for their strings. These are in the same directory.
3. A deriver class TestHelpTopicDeriver that creates a topic with title "Label for help_topics_derivatives" and body "Body for help_topics_derivatives". No translations.
4. A topic defined in the help_topics_test.help_topics.yml file with label "Test direct yaml topic label" and body "Test direct yaml body". No translations.
5. Two topics from the TestHelpSection with English titles "Foo in English title" and "Bar in English title", and German text that we're discussing what it should be in the previous two comments. Both the text and translations are provided in that class.
So.... I think we have enough topics and translations to test all of the above if we are careful, and we should avoid using real words as search terms that might pull in real topics. So, we might want to rewrite some of the topic text so it is full of enough fake words that we can do all the tests.
You are probably/hopefully asleep (??) so I will make a new patch in a few minutes...
Comment #119
jhodgdonI have to run out but here's a test that does all of that... it is not passing yet, and I am not sure why... I can look at it again tomorrow or you can andypost...
Comment #121
jhodgdonAs far as I can tell, this is a real bug, either in the test or in the code. When I debugged this test, I could see that during the cron runs,although we asked for the DE translation of the test topic, the text from the Twig template was not actually translated using the PO file that is in the translation test.
Something is going wrong... I'm not sure what. The HelpTopicTranslationTest, which uses the same PO file and the same topic file, works -- you can see it in the browser output if you run interactively -- the test topic is definitely translated there. But not when we try to render it for searching. So, something is definitely wrong with either the test environment or the code that is rendering topics in different languages to send to the search index.
Meanwhile, here is an updated patch with a test that we need to get to work. I think this is now a very thorough test, but it isn't passing, so...
Comment #123
Berdir> So, something is definitely wrong with either the test environment or the code that is rendering topics in different languages to send to the search index.
Isn't that what I said in #106? :)
> I'm also surprised that different-language indexing like that works. We just change the *default* language? But \Drupal\language\ConfigurableLanguageManager::getCurrentLanguage() statically caches the result of language negotiation?
You need to set the current language instead.
I wasn't even sure that we have a way to do that. We do have \Drupal\Core\Language\LanguageManagerInterface::setConfigOverrideLanguage(), which is for example used in user_mail(), but that doesn't actually cover string/interface translation. That does seem to be set in \Drupal\language\EventSubscriber\LanguageRequestSubscriber::setLanguageOverrides() for example ($this->translation->setDefaultLangcode($langcode);), but that method isn't actually on an interface. It is on \Drupal\Core\StringTranslation\TranslationManager::setDefaultLangcode, and we obviously expect it to exist. Fun stuff...
Comment #124
jhodgdonObviously, I didn't get what you said in #106. Thanks for posting it again. :) I'll see what I can do...
Comment #125
jhodgdonThanks again for your help @Berdir -- that did it. Test now passes locally.
After I made this interdiff, I removed a blank line in the test file that Coder complained about in the previous test also. Hope that is OK not to get it into the interdiff.
I wasn't sure if it was OK to specify a class passed into the constructor for the TranslationManager, but as pointed out by Berdir, the method we need to call isn't on an interface...
Comment #126
jhodgdonOh, and I figured out why the "Writing good help" topic showed up in the earlier tests when we searched for "ABC". The reason is that the test topic adds the Writing good help topic as Related, which makes the title of the test topic show up on the Writing good help topic page. So when it is indexed for searching, the text "ABC" is included.
We probably shouldn't do that. I think we should refactor the help topic tests so that the tests are not using production topics at all, and the test topics don't link to production topics. Created an issue for that. Meanwhile, these new tests are only relying on the test topics and test classes.
Comment #127
jhodgdonComment #128
jhodgdonCan we get this to RTBC? I don't think we should postpone it on #3075695: Move search_index() and related functions to a service, which should not be a prerequisite to getting this done, as it affects NodeSearch too. Thoughts?
Comment #129
larowlan100% agree
Comment #130
andypostI think it really for commiter's eyes and surely tests get changes in future.
The scope of issue to add search which could be polished endlessly. Current tests coverage is enough.
Comment #131
jhodgdonReroll of previous patch due to #3076348: Refactor help topic tests so they do not use production topics. Only change was in the context of one of the help topics in the test module. Here's a straight diff of the two patch files for reference:
Comment #132
larowlanThis is looking great, and its exciting to see it nearly ready and the amazing progress help_topics has made in this cycle 🥇
Couple of observations.
is this needed? It doesn't look like we have any config to go with this plugin - the node one does, but I don't see any here?
nit: is the comma required after permissions in this sentence?
nit: assumes not assume?
We don't have an index on this column in the table schema. Should we be adding one given we're using it in a groupBy and a where clause?
we could do this inside the existing array_filter with a custom callback.
this is executing this for every $item in $found performant, can we fetch the full result set in one query for all items in found? Looking back at earlier comments, I asked for this before and you replied
From what I can gather, the query is just to retrieve the plugin ID and type for each sid - so we could conceivably replace it with something like this outside the foreach loop.
and then loop over that or call
->fetchAllAssoc('sid');
on it if we need it keyed by sidThen we'd have plugin_type and ID available in an array keyed by sid. I think 🤔
is it possible that the search results table in the database could contain references to help topic plugins that no longer exist (e.g. are from modules that have since been uninstalled, or in the case of the contrib module - have been deleted? If so, we should wrap either this, or the code inside ::getSectionPlugin in a try/catch that catches a PluginException. The same applies to the call to ::getSectionPlugin from within ::updateIndex
should we put an
assert
call here to ensure that $topic['cacheable_metadata'] is an instance of an appropriate class for this method call/typehint?is there an alternate code path here when the state flag is not set where we do nothing and return early?
should we use
\Drupal\Tests\system\Functional\Cache\AssertPageCacheContextsAndTagsTrait
here?Comment #133
larowlanManual testing:
After installing standard profile and then enabling help_topics, I was unable to search for any content until I ran 'reindex site' from admin/config/search/pages - does that mean we're missing a hook_install or similar to prime the database when search module is enabled?
After doing that and then running cron, got search results.
Looks great 🎉
Comment #134
andypostFeedback on #133 - I bet running indexing on install is bad idea, moreover that's a search module territory so install of help topic module should not check that search installed and try to re-index
Feedback on #132
1. it is required because each search plugin use own settings, which require schema https://git.drupalcode.org/project/drupal/blob/8.8.x/core/modules/search...
2-3 fixed 3 and no sure about 2
4. not sure because I expect that amount of topics will not be huge, and I can't see difference on "tens of records"
5. fixed
6... TBD will try to check tomorrow
Comment #135
jhodgdonRE #133 -- we are using the core Search module.
What should happens is that after enabling all the modules, you would need to (a) do a cache clear and (b) run cron. Then you should be able to search. Otherwise nothing is in the index. That is just how the Search module works.
I'm out of Drupal-land until probably November. Sorry...
Comment #136
oknateI'm not 100% familiar with this new functionality, but I tested it out AFAIK and it works. I was able to run cron and then search for the help topics.
Notes:
1. It's not clear why search isn't working before cron has been run. If there are no indexed items, it would be good to display a warning that search items haven't been indexed. This could be follow-up material, so as not to block this issue.
2. There's an assertion for counting elements:
3.
$this->assertLink()
is deprecated, should be replaced with$session->linkExists()
.4. Adding a few coding standard fixes.
Comment #137
oknateAnother small change:
$this->assertText() is deprecated
Comment #138
jhodgdonRegarding #136 -- that is just how the core Search module works. You need to run cron in order to index items for searching, whether they are Node module content items or (now) Help items. We never had a warning message about the Node search, so I don't think it is necessary to have one for help search either. If you go to the Search settings page, you will see the indexing status and it will tell you cron needs to be run (possibly several times if there is a lot to index).
Comment #139
jhodgdonYou could also look at the Status Report page and see if, before you run cron, there is a notice there about the search index not being fully indexed. I think if there is not something there already, the Search module should be updated.
Comment #140
Ghost of Drupal Pasttl;dr: I renamed plugin_type to plugin_id.
This issue is monumental, it has the potential to change the very perception of Drupal as it will basically ship with the manual built in. Huge congrats to everyone getting it this far, let me see whether I can further it a little bit.
I was nitpicking at first and made small fixes (in
findResults
the indentation was telling it's slightly unreadable, I made the query a bit better while at it and moved the not empty condition into the query, I renamed a join to innerJoin for easier understanding, inmarkForReindex
I dropped an unnecessary fetchAll, and an unnecessary empty test because foreach over an empty array is just as good, another empty test inremoveItemsFromIndex
because instanceof works with any type of variable, simplified and fixed the loop inHelpTopicSearchTest::setup
it wasn't incrementing $num_runs, all nitpicks) until I arrived to'plugin_type' => $plugin_id
Wait a minute, what?
'plugin_type' => $plugin_id
?? I look more and see$plugin = $this->helpManager->createInstance($plugin_type);
and I am like, OK that's not a plugin type, that's a plugin id. So I did a rename withgit diff 8.8.x |sed s/plugin_type/plugin_id/g
so I can guarantee nothing was left out of the renaming, submitted it into a testing issue and it came back green. So I am submitting it for consideration.Comment #141
oknateNice catch on \s\plugin_type\plugin_id. This should change also:
I recently added a related issue #3083507: Standardize "plugin ID" in doc comments instead of "plugin_id", so I would suggest calling it "plugin ID" in the comment.
Comment #142
Ghost of Drupal PastHow deep does the rabbit hole go? There are three plugin types and associated IDs in this patch and boy, are their names confusing. (Reminder: a plugin type is like migration source or help section.)
type
for legacy reasons. TheHelpSearch
search plugin already had theSearchInterface::getType
function implemented as$this->getPluginId()
but it still used to call$this->getPluginId()
everywhere instead of the more semantic$this->getType()
. Old version:':type' => $this->getPluginId()
, new version':type' => $this->getType()
. That certainly looks better and I didn't plan on it, I was just doing some search-and-replace, all the methods were there and I didn't touch the method names. Same is true for the other two. (Note this issue is working on renaming some$type
to$plugin_id
but the database columns and the method name stays so there is no conflict.)section_plugin_id
. Results in$this->getSectionPlugin($section_plugin_id)
and again I was not planning on the new variable matching up with the relevant method name.topic_id
. Results inrenderTopicForSearch($topic_id
. Isn't it nice how these align?As visible from above, I am really happy how this turned out, I hope others will be just as happy. Interdiff is against 137 (140 is just me confused, ignore that).
And since I am making a list, let me make a nice formatted list of the small fixes in #140 and of course here as well:
findResults
a little more readable (the indentation was telling it's slightly unreadable) and I moved the not empty condition into the queryjoin
in there toinnerJoin
for easier understandingmarkForReindex
I dropped an unnecessaryfetchAll
,markForReindex
I dropped unnecessary empty test because foreach over an empty array is just as good,removeItemsFromIndex
becauseinstanceof
works with any type of variableHelpTopicSearchTest::setup
.= NULL
for$account
in the middle of the argument list, I removed it.Comment #143
Ghost of Drupal PastOne more. Why does
HelpTopicSearchTest::setup
compare the remaining to total? It should test for zeroness. Now it does. With an assert.Comment #144
Ghost of Drupal Past*frown* where are my files?
Comment #146
andypostnit, two `;;`
Comment #147
Ghost of Drupal PastThat fail is fixed, interdiff is against #142. Patches up to #137 were looking for search type "foo" items in the database of which there were zero so the remaining and the total were always equal... because the search type is always help_search (mayhaps someone mixed up search type with topic id which now hopefully won't happen because I renamed things). So the loop was testing $num_runs < 100 which always succeeded and then $indexed was always TRUE after the first loop so only one iteration ran and that was enough since cron allows 100 items to be indexed and we only had 19. But, you know, it was not testing the
indexStatus()
method much. And then I fixed the condition was actually made cron run 100 times because there were always 19 items remaining as the search_dataset didn't contain any "foo" items... Now that's fixed and I added two asserts to see the it finishes in less than 100 iterations and it actually didn't leave anything behind.Comment #148
vijaycs85Overall the changes look great(+1 to RTBC). I did manual testing and didn't see anything unexpected.
Few observations:
1. search is somewhat slow for like 13 items on a standard profile installed core-only site: after a cache clear it takes about 5sec and with cache, it takes 2.1 sec.
2. Small alignment issue with the help link on the empty search result page.
Here are a few screenshots of the UI changes introduced by this issue.
Comment #149
jhodgdonThe speed and alignment issues are coming from the core Search module, not from this patch.
I have carefully reviewed the code and I am +1 for RTBC. I don't think I can set it to RTBC though, since I wrote a lot of the current patch.
Comment #150
Amber Himes MatzI've also reviewed and tested the module. I only have a couple of UX nits which I don't think are necessarily blockers. They are:
1. It is somewhat jarring to enter a search at admin/help in the Seven (or administrative theme), then have the search results show up in the default theme (e.g. Bartik), then click on a help topics result going back to Seven (admin theme).
2. If a user has the search block enabled and placed in the default front-facing theme and they perform a search, the linked text "Search help" will appear near the form. (See also this screenshot from #148 https://www.drupal.org/files/issues/2019-10-01/2664830-148-search-result....) This text in this context means "Read some help documentation about searching on this site." But now we have a search form on admin/help would could quite conceivably be titled "Search help". And in this context it would mean, "Search for help topics." Having these two meanings of "Search help" co-exist on a site could lead to confusion. (It certainly tripped me up when I was testing.)
These 2 UX issues notwithstanding, the code and functionality looks fine to me and in light of the other recent in-depth reviews, I am in favor of an RTBC, using the patch in #147.
I can also open follow-up issues on the 2 UX issues I mentioned above.
Comment #151
alexpottI've had a long discussion with @chx and @larowlan about whether this is the right place to be doing rendering and language switching.
This all feels very tricky to me. My original thought was that language setting and rendering for search should be the responsibility of the HelpSearch plugin but @chx pointed out that if this prepping content entities for help search then the language switching would be different. I think this is a good point and means we probably have this on the correct level. (As much as it pains me to see the renderer and language manager and everything injected here).
I think the language swapping here might need to borrow more from \Drupal\commerce\MailHandler::changeActiveLanguage - it does this:
Not sure though. It's a shame that there isn't code on the language manager to handle swapping the default language. There are some interesting protections in that method where $langcode is not valid and the services are not quite as we expected them.
Are we sure that the complexity here is necessary? I've changed this to
and
Drupal\Tests\help_topics\Functional\HelpTopicSearchTest
passes just fine.We lose the double render and renderPlain does executeInRenderContext() already.
I'm not a huge fan of returning arrays with magical keys on an interface based API. Normally I'd reach for a value object for this. @chx pointed out that these values are consumed immediately and therefore he feels a value object is not needed.
If we add
->fields('hsi', ['section_plugin_id', 'topic_id'])
to the find query then we don't need to query the same table again. Less queries ftw.Comment #152
alexpottAS I have it locally here's #151.2 and #151.4 addressed.
Which only leaves the #151.1 questions about whether we are doing language swapping correctly and safely to do.
Comment #153
alexpottLolz lolz lolz. Learnt a new thing today. So when you run cron in test or via the browser (same thing)... you're executing cron in a render context so that means the point in #151.2 is valid - however if you run cron via drush you are not. Therefore it fails. So probably do need the RenderContext code. We also need to open an issue to somehow execute cron via drush and via the browser in the same environment.
Comment #154
alexpottSo here's a funny problem. If you install
Comment #155
alexpottI think #154 shows that we need a
hook_modules_installed()
as hook_rebuild is not triggered when installing a module (even though its docs kinda imply it is. It's not called on uninstall either. This all implies the patch is missing test coverage.Comment #156
jhodgdonI am +1 on the changes in the latest patch (#153).
Regarding #154... It seems like the bug is that hook_rebuild is not triggered when you install a module? But we could definitely add hook_modules_installed() and add a test here that replicates the steps in #154.
I remember discussing this, I guess in just Slack and the discussion didn't get into the issue, when I was developing this patch, and I don't remember with whom. I was concerned because there are several cases where we would need to reindex the help topics, because the content of topics might have changed:
- Install/uninstall a module/theme
- Add/remove a language
- Update a module/theme
- Update/import/remove translations
- Update config [if someone is using the contrib config_help module]
The consensus of that discussion was that we could not possibly detect all of these cases, so we should just trigger reindex whenever there is a cache rebuild, and if someone does any of these things and the system doesn't automatically trigger a cache rebuild, most likely the person who did the thing would rebuild the cache anyway.
So... I think we have 3 options:
a) Decide that a cache rebuild should be happening whenever a new module is installed, and make that happen in Core.
b) Implement hook_modules_installed() with the same body as hook_rebuild() we have in the current patch.
c) Leave it as it is and assume someone who found things not working on their site, or made any major changes, would probably rebuild the cache.
Thoughts?
Comment #157
alexpottI think we need to do (b) since the behaviour in #154 is very unexpected and we can't do (a) because doing a full cache flush on module install /uninstall is extremely expensive and we've not done it since before 8.0.0.
Comment #158
jhodgdonIt sounds like we need both hook_modules_installed() and hook_modules_uninstalled() then? Because if a module is uninstalled and the cache isn't flushed then (really???), we definitely need to at least flush out the topics that were provided by the module.
Comment #159
alexpottWe don't call drupal_flush_all_caches() nope. Just checked D7 too. drupal_flush_all_caches() is only called for module enable and disable when it's done via the UI so in a way D7 is not different either.
And yes we definitely need a test to show that once a module is uninstalled it's help topics are no longer been displayed - even if cron has not been run.
Comment #160
jhodgdonAh, so if you install/uninstall a module *in the UI*, then caches are rebuilt, but not if you use Drush or another programmatic method. That makes sense. That is probably also true for some of the other things that need to trigger reindexing help, such as adding/removing a language or updating translations... and I don't think we can even really detect every possible thing that should trigger a reindex.
And as a note, your idea of adding a test to verify topics are removed even if cron hasn't been run will not pass with the current code -- the list of topics that should be included in the index is only currently updated during cron runs.
So... I think we need to do something else here, rather than trying to add more hook implementations, and testing for all those cases. I'm going to open a discussion in Slack, and then report back when we've decided something...
Comment #161
jhodgdonWe had a rather long discussion about this on Slack, with andypost, wimleers, pwolanin, and jhodgdon participating.
Some thoughts:
There are several reasons that the list of which topics/languages need to be in the Help Topics Search Index could change. It is probably possible to detect all of them (if we're sure we've figured out all the reasons), but would require quite a bit of code complexity to do so. We are currently only recalculating this when hook_rebuild() is triggered.
There are also several reasons that the indexed text for a given topic/language could change, including updating the Twig file source, adding/updating/removing translation strings, and (if the config_help contrib module is being used) updating configuration. Again, these could probably all be detected, but it would require a bit of code complexity to do so. We are currently triggering a reindex of topic text on hook_rebuild() and when the "Rebuild search index" button on admin/config/search/pages is clicked.
What I think we should do:
a) Rebuild the list of topics on each cron run. This should not be very expensive, as it's mostly asking for cached things (like lists of help topic plugins). That will ensure that if modules are added/removed, we detect their topics even if a cache rebuild is not done.
b) Also trigger full text reindex when the plugin cache is cleared for the Help Section plugins.
c) Document for admins that they should click the "Rebuild search index" button on admin/config/search/pages if they have updated translations or made other changes that they think might affect help topic text.
d) [Maybe??] Consider adding to the end of each cron run that we cycle through topics that were indexed the farthest in the past, and reindex them. This way, all the stale stuff in the help topic index would eventually be reindexed.
Comment #162
jhodgdonIn order to implement this, we would need to:
1. Separate HelpSearch::markForReindex() into two parts. One would recalculate the list of topics [call this updateTopicList()], and the other would mark all existing topics for reindex.
2. In HelpSearch::updateIndex(), always call updateTopicList(), but only call markForReindex() if the state variable is set.
3. Think about whether we should call updateTopicList() from hook_modules_uninstalled() so that the list of available topics is immediately reduced when a module goes away. And is there a hook_themes_uninstalled() too? Should be done there too. Can we make this method static, so it can be called more easily from this hook? HelpSearch is a plugin...
4. Add to some documentation.
Comment #163
alexpottSo with the current patch nothing shows up until you run cron. This kinda feels okay but at the very least I think we need a hook_modules_uninstalled() to remove topics that are no longer available. We don't want to be able to search and then end up with help for things that we can't do. This is inline with what node does on node create and node delete. Node create - it's not searchable until cron has run but it is removed the moment you delete it.
One thing I wonder is does the current code already cope with this - it doesn't remove it from the search index but I think in order to display a result it has to be able to load the topic which it will no longer be able to do.
I think we need to start with testing our assumptions. I.e. add the following sort of test to HelpTopicSearchTest
Comment #164
andypostAs there's no way to display custom message (from plugin for example to report why search cannot be done) https://git.drupalcode.org/project/drupal/blob/8.8.x/core/modules/search...
I think it only doable when building/preprocessing results https://git.drupalcode.org/project/drupal/blob/8.8.x/core/modules/search...
Another idea is do additional check in
\Drupal\help_topics\Plugin\HelpSection\HelpTopicSection::renderTopicForSearch()
or override\Drupal\help_topics\HelpTopicPluginManager::createInstance()
to exclude unavailable topics from build (as they remain searchable)Comment #165
jhodgdonThat is true that the topic will not be displayed on a search (RE #163). However, you would get fewer results per page than expected, and could get a problem where all 10 of the first results were non-existent topics and thus a "no results" result even though the next result would have been valid. So I think you are right, we need to remove topics when modules are uninstalled. This would be implemented by #163 item 3.
Regarding #164, I don't really think it's necessary... or if it is, it's needed in core Search in general and should be a follow-up issue.
Comment #166
alexpott@Charlie ChX Negyesi pointed out in Slack I broke postgres by not reading the great comment. This patch fixes that.
Comment #167
jhodgdonPostgres passing ++
So it looks like we still need to take care of the module install/uninstall stuff. In particular, I think we should do the following [slightly different from my last suggestion, and incorporating some other comments]:
1. Separate HelpSearch::markForReindex() into two public methods. One would recalculate the list of topics [call this updateTopicList()], and the other would mark all existing topics for reindex [markForReindex()]. Note that markForReindex() is a public method called by the core Search module when the user clicks the "Rebuild search index" button, and it needs to also call updateTopicList().
2. In HelpSearch::updateIndex(), always call updateTopicList() [every cron run we will recalculate the list of valid topics, no matter what].
3. Hooks/events/etc.:
- hook_rebuild() should call the public markForReindex() method directly (let's not use a state variable unless we have to).
- hook_modules_uninstalled() should call updateTopicList(), so that invalid topics are immediately removed
- when plugin cache is cleared for the Help Section plugins, call markForReindex()
4. Add to the docs so admins know about the "Rebuild search index" button and/or clearing the cache.
5. Add to the tests:
Comment #168
jhodgdonUpdating status for accuracy (see previous comment). I'll go ahead and make a patch for this later today.
Comment #169
jhodgdonTo start with, here is a reroll of the previous patch, which did not apply cleanly for me on the latest Core.
Comment #170
jhodgdonOK, here's a new patch that takes care of #167. Search test passes locally. A couple of questions/notes:
a) In the previous patch, we used a state variable to keep track of whether we needed to update the topic list and/or mark for reindex. I have eliminated that, because we need to have an immediate action if a module/theme is uninstalled, and it didn't make sense to me to have the state variable sometimes and not use it at other times.
b) I'm not quite sure about what I did to implement this part of #167:
I implemented it like this, in the HelpTopicSection plugin class:
(calling a new function in help_topics.module). Also added docs to the Searchable interface so it tells implementers they should do something similar...
Two questions about this:
1. Is this the right place to respond to "plugin cache is cleared"? I think it is.
2. Is it OK to call this function? I know we generally want to avoid calling functions like this, but doing it with proper dependency injection would I think not work, because some of that function only executes if the Search module is enabled. Here's the function body:
I just don't think this can be done via dependency injection, since the search page repository service only exists if the Search module is enabled.
Thoughts?
Comment #171
jhodgdonThe topic in this patch is going to need a reformat for #3069109: Replace help_topic meta tags with front matter. Rest of the patch can still be reviewed. I'll take care of that shortly...
Comment #172
jhodgdonHere is a straight reroll of the previous patch, with just the front-matter change made to that one topic about how to set up searching for help. I haven't tried to run the tests locally; let's see what the bot says instead...
Here's the section that changed:
Everything else should be the same, except a few parts of files that were removed in that other issue and patched here are gone.
Comment #173
jhodgdonComment #174
Amber Himes MatzI'm putting the UI through its paces. If I uninstall help_topics via Drush and refresh admin/help, I get a "The website encountered an unexpected error. Please try again later."
1. Install Drupal 8.8-dev
2. Apply patch in #172
3. Enable help_topics
4. Re-index site
5. Run cron
6. Test search box in admin/help -- everything is fine up to this point.
7. Uninstall help_topics
8. Refresh admin/help
9. White screen with "The website encountered an unexpected error..."
10. Log message immediately after uninstall:
Comment #175
Amber Himes MatzSame result uninstalling via admin/modules/uninstall UI.
Comment #176
andypostI think it is no go to use function within object's method
This code bit confusing me because both actions needs search page entity (plugin) to exists but /admin/config/search/pages allows to disable search page as well so
- makes sense to move this to plugin.manager.help_topic as 2 methods for example to separate plugin lookup from actions
Comment #177
jhodgdonRegarding #174... It is not this module's fault if Drupal doesn't automatically rebuild the routing tables after a module is uninstalled or a search page is deleted.... Definitely the route to the search page would not exist if the module is uninstalled and therefore the search page doesn't exist. That seems like a problem in the Search module or Drupal Core.
Regarding #176 -- I don't know what else to do. We cannot inject the service into these classes unless the Search module is installed, which it may not be.. See comment #170. No one bothered to answer that question... but I don't think there is any way around having some procedural code... I guess we could use $this->container->service() but it will still be lame, and I would rather contain the lameness in a central function call than scatter it in 2 or more classes? Not sure. Anyway... If you have a better idea I am open to it.
It is probably too late to get this done by Wednesday now. Not sure what to do but with the time zone difference between Russia and western US, I don't think we will have time to finish this to everyone's satisfaction. :(
Comment #178
Amber Himes MatzOk, regarding #174-5, I will add this to my list of follow-up issues to open.
Comment #179
andypostIf the search page (config entity) is disabled then search admin page shows "Does not use index"
So the helper function should check the status (added comment)
Also this function only depends on presence of storage handler for pages to access plugin & state of config
Implementation for "immediately re-index" could use suggestion from #123 and implement batching like locale module doing (to import translations) could be done in follow-up because of complexity and tests (let's keep scope)
I think it ready to go
Comment #180
Amber Himes MatzI've tested the latest patch (#179) in the UI and it functions just fine. I will be opening follow-up issues for the UX issues I raised in #174 and #150.
Just waiting for the testing to complete the check on #179, but pending a final code review (maybe from @alexpott?) I would be +1 in favor of RTBC.
Comment #181
vacho CreditAttribution: vacho at Skilld commentedI review this issue after long months. After review, I see that the main last issues were solved.
1. Install - uninstall - install problem doesn't exist anymore.
I test it manually
2. Indexes, re-index
Works as I expected, a hook re-index all searchable pages.
3. Search result
After manual test works fine with olds and a new module
RTBC +1 for this issue.
Note:
- After module help_topics install is needed to run the cron.
Comment #182
Amber Himes MatzThank you for the review @vacho and for the new patches @andypost and @jhodgdon!
I think the functionality is there. We've had several pairs of eyes on this throughout. And we can open follow-up issues as needed to refine the UX. Those issues are is in the search module anyway. As for what help topics module is doing, we have a functioning search for help topics.
I'm setting this to RTBC and we'll see if we can get this in by the deadline.
Comment #183
jhodgdonRE the UI in #179, User search simply doesn't use the index. It is not because it is disabled, it is because User search just doesn't use the index even if it is enabled. But the interdiff in #179 looks OK to me, aside from a nitpick in the grammar of a new comment:
I edited the patch file directly to change this one comment to
"Only update the search index if there is an enabled help search plugin."
Leaving at RTBC since this is just a simple nitpick.
Comment #184
jhodgdonActually, though, with the latest patch we can inject the necessary services into a class so we don't have to call the function in help_topics.module from within a class. Temporarily bumping this back to Needs Review. I'll see if I can make a new patch within an hour or two that will do just that.
Comment #185
jhodgdonOK, here's a patch that removes the call to the function in help.module in the HelpTopicSection plugin -- the function calls in help_topics.module are still there (and I think they are appropriate). In HelpTopicSection, this is replaced by a dependency-injected 10 lines or so of code. Thoughts?
Comment #186
jhodgdonAs a note, if we wanted to make this a little more compatible with other search back ends (like Search API module), I think we would want to define some kind of an event or hook that could be triggered to say "The searchable topics list needs to be rebuilt" or "The text of topics need to be reindexed". Other than that, Search API should be able to use the methods on SearchableHelpInterface to discover and index the topics.
The reason this hasn't been an issue for Node searching in Search API is that there are already hooks/events/whatever that say "A node has been created/updated/deleted", so Search API can respond. But in the case of help topics, they aren't entities that are being indexed for searching, so there aren't clear hooks/events when they need updates.
So... I think such an event or hook could be defined later.
Comment #187
andypostI think that's most clean approach
Each plugin "knows" own plugin manager and interface declares search support now
moved the interaction to search to topic plugin manager interface
it was broken - plugin should be taken from page
Comment #188
jhodgdonThat seems better indeed. +1 for RTBC if the bot agrees.
Comment #189
andypostSymfony\Component\Routing\Exception\RouteNotFoundException: Route "search.view_help_search" does not exist. in Drupal\Core\Routing\RouteProvider->getRouteByName() (line 201 of /var/www/html/web/core/lib/Drupal/Core/Routing/RouteProvider.php).
Reproducible error when I disable->enable any search page at
/admin/config/search/pages
Comment #190
jhodgdonAmber saw that in comment #174. I think it is a core search module problem?
Comment #191
jhodgdonUnrelated: I was looking for help_search in the patch (to see if we are doing something obviously wrong), and found this comment
The namespace here should be help_topics not help_search. [doc block for constructor in the HelpSearch plugin class]
Comment #192
alexpottThe hook_rebuild only gets called on drupal_flsuh_all_caches()... that's supposed to call the clearCachedDefintions... it not currently happening because this method only exists on plugin managers not plugins (which this is)
Here's a patch that has tests and works. It also reduces the complexity (a little) compared to #187 and #185. The one bit off additional complexity is that we need to decorate the Help section plugin manager from the help module in order to mark search for re-index on the right level.
Comment #193
alexpottNo idea why I uploaded the patch twice... but they are the same :)
Comment #194
jhodgdonNew patch/interdiff looks good to me, assuming bot agrees. New test lines look good too.
Comment #195
jhodgdonAlso as a note about #174/#189, @andypost found this related issue.
Comment #196
andypost@alexpott thanks a lot, it looks much cleaner!
But ... it does not respect "status" of search page (core search is not supposed to reindex if no search pages enabled (see #179 screenshot)
Comment #197
jhodgdonI don't think that matters, actually. It is possibly a bit inefficient to reindex if help search is not enabled, but it won't actually hurt anything.
The screenshot in #179 actually shows something else: that User search never uses the Search index (it does its own direct queries on the user module database tables). It is not necessarily wrong, just a bit inefficient, to have Help indexed or the Help topics table populated, without a search page for help being enabled.
Comment #198
Amber Himes MatzApplying the patch from @alexpott in #192, I get the following error when I try to uninstall help_topics.
Steps to reproduce:
1. Install Drupal 8.8-dev
2. Apply patch in #192
3. Enable help_topics
4. Re-index site
5. Run cron
6. Test search box in admin/help -- everything is fine up to this point.
7. Uninstall help_topics with drush pm-uninstall help_topics. Error.
Comment #199
jhodgdonLooks like it could maybe be related to #2901590: Investigate route rebuilding problem wrt Search module ? Or... Oh! I think it's probably because we have a hook_modules_uninstalled(). We should probably put an if() in there and only rebuild the index if help_topics is not in the list of modules that is uninstalled. Doh!
Comment #200
andypostAt least it fixes uninstall
Comment #201
jhodgdonWow, that is weird. It seems like a plugin provided by help_topics module should not have any of its methods executed on the cache clear that happens when help_topics module is being uninstalled. ?!? That seems like kind of a core bug, really. But anyway.
It seems like we might still need/want to do something similar in the hook_uninstall()? Something like this...
Comment #202
andypostLooks cache clear issue is known bug
- #2206347: Use event system in ModuleHandler
- #3056604: Add/remove event listeners provided by a module on module install/uninstall
Comment #203
jhodgdonOK, this is totally untested... Amber keeps coming across that WSOD from routing if you disable the help_topics module and then try to go to admin/help. This is bad. We are pretty sure it is coming from the Help Search block. So, here's a patch that hopefully fixes the problem, by making sure that the search form in the search block is not trying to search disabled or nonexistent search pages.
I haven't even tried this out but here it is... will maybe work...
Comment #204
Amber Himes MatzOk, on this latest patch in #203, I tested it and both uninstall errors are resolved! (Reported in comments #174 and #198.)
With a code review on the latest changes, I would be +1 RTBC. And will be willing to RTBC with 1 more code review please.
Comment #205
andypostthat's fixes uninstall, the remains will be addressed in #2901590: Investigate route rebuilding problem wrt Search module
Looking on #3075695: Move search_index() and related functions to a service
PS: I don't think we need install/uninstall test here
Comment #206
alexpottI think we need test coverage for this. I'm in process of adding it.
Comment #207
alexpottThe test added by the patch catches both #198 and #203.
Comment #208
andypost@alexpott Thanks for test! let's get this in)
Comment #209
jhodgdon+1!
Comment #210
alexpottIn #150 @Amber Himes Matz discussed 2 UX concerns - before we can commit this we need those issues created and on the roadmap for stable - so we don't forgot about them.
Comment #211
jhodgdonRight! Added
#3086794: Search results pages plugins could display results in administrative theme
#3086795: "Search help" link in search form is ambiguous and confusing
and I'll add them to the Roadmap as well.
Comment #212
alexpottOh noes... I've just realised we have change outside of the help topics module here. :(
This either needs to go in blocking issue or be resolved differently. Patches against experimental modules shouldn't make changes to non-experimental code (unless it's part of removing the experimental ness).
Let's see if there is another way.
Comment #213
alexpottYay for having test coverage. So here's an acceptable work-around for now. But the problem is caused by the fact that search_form_block plugins should add a config dependency on their page_id configuration entity. So we should create a search issue for that and then we can remove the enforced dependency.
Comment #214
alexpottCreated #3086824: SearchBlock plugins need to add in dependencies on the search page they submit to
Comment #215
jhodgdonInterdiff looks good, assuming your uninstall test still passes. Back to RTBC (provisionally). I (obviously) wasn't aware that we couldn't make changes outside of help_topics, but that does indeed make sense!
I will also add a note about the doc block I fixed in that other issue.
Comment #216
jhodgdonActually created #3086832: SearchPageRepositoryInterface doc block error for the doc block issue.
Comment #217
jhodgdonActually, this patch will not work for all cases.... It takes care of the case of uninstalling Help Topics module, but it doesn't take care of the case of disabling the search page. We need to fix core Search module for that. I added a note to the other issue. I don't think the config dependency is the right/only fix -- we need what was in my earlier patch.
But if we can't take care of the real problem in this patch... not sure if we can have this as RTBC with this bug outstanding?
Comment #218
jhodgdonDiscussed this with alexpott in Slack.
The conclusion:
- Having a WSOD on admin/help when you disable the Help Topics module was a blocker. That is fixed by this patch.
- Having a problem when you disable the help search page on admin/search/pages is not a blocker. So, created this follow-up issue for the core Search module: #3086846: Deprecate ability to disable search pages (no reason for it and causes problems and code complexity)
Comment #219
Amber Himes MatzI've added 2 follow-up issues related to the UX issues I raised in #150.
- #3086856 Search results should display in the theme in which the form was submitted
- #3086860 The phrase "Search help" now has multiple meanings and could create confusion
Comment #220
Amber Himes MatzOk looks like @jhodgdon had the same idea. I closed my follow-up issues as duplicates. The active follow-up UX issues are already set up as child issues of this one.
Comment #221
larowlanshould we have an index on the permissions column?
Comment #222
larowlanDiscussed #221 with @Charlie ChX Negyesi in slack who indicated that the query wasn't going to be helped by an index, given its current nature
Comment #223
larowlanComment #225
larowlanCommitted f702e8c and pushed to 8.8.x. Thanks!
Published the change record.
Creating a 'mark help topics module beta stable' issue