Problem/Motivation
$build['#attached']['drupal_add_feed']
This is a) a confusing API and b) we don't want to keep drupal_add_feed forever.
Proposed resolution
- Rename all drupal_add_* methods to use an underscore (like css/js)
- Make it possible to use $build['#attached']['feed|html_head|html_head_link']
Remaining tasks
User interface changes
API changes
- '#attached][drupal_add_feed' => 'feed'
- '#attached][drupal_html_head' => 'html_head'
- '#attached][drupal_add_http_header' => 'http_head'
drupal_add_region_content() and drupal_set_region_content() are also removed - these were dead code already.
Original report by @username
We removed direct calls to drupal_add_html_head_link() but didn't deprecate either that or drupal_add_feed().
Opening this as a critical follow-up, we realised this during the DrupalCon Amsterdam discussion about page rendering.
https://api.drupal.org/api/drupal/core%21includes%21common.inc/function/...
https://api.drupal.org/api/drupal/core%21includes%21common.inc/function/...
Additionally, we decided to formalize the #attached support in https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Page%21Re... so it doesn't use the function names (i.e. you can use #attached['head_link'] or similar). Looks like dynamic function support has already been removed there.
| Comment | File | Size | Author |
|---|---|---|---|
| #26 | 2350877-25.patch | 26.88 KB | hussainweb |
| #26 | interdiff-21-26.txt | 465 bytes | hussainweb |
| #21 | 2350877-21.patch | 26.88 KB | wim leers |
| #9 | interdiff.txt | 5.41 KB | dawehner |
| #6 | interdiff.txt | 20.21 KB | dawehner |
Comments
Comment #1
dawehnerI got distracted quite a bit, so now this also removes drupal_region_add_content() which does not work anymore anyway.
Comment #2
dawehnerThere is also a tag for it
Comment #3
dawehnernice tag!
Comment #4
catchComment #5
wim leersComment #6
dawehnerComment #7
ianthomas_ukIs this also the place to take account of HTTP Headers? #2307723: Provide a replacement for drupal_add_http_header
Comment #8
ianthomas_ukAh, I see you've already added support for
['#attached']['http_header']in the patch, but we should also renamedrupal_add_http_header()so people don't think they can call it directly.Comment #9
dawehnerSo yeah let's do that.
Comment #10
dawehnerUpdate IS
Comment #11
dawehner@catch asked to drop the support for it.
Found one documentation instance.
Comment #12
catchLooks great now!
Comment #13
alexpottWe're going to need a CR for this.
Comment #14
alexpottComment #15
catchhttps://www.drupal.org/node/2160069 was completely out of date, only dealt with one function, and still used url(). Since we haven't sorted out the url API yet, I left stub examples in there, but covered the other functions.
Comment #16
catchComment #17
alexpottLooks unnecessary.
Comment #18
hussainwebI am attaching a reroll, plus changes as per #17. The interdiff is only for changes in that comment, not for the reroll.
Comment #19
dawehner+1
Comment #20
wim leersFixed the only remaining todo and the following nitpicks:
This already is in now :)
"invalid attachments in a render array"
s/attachmend/attachment/ :)
That's not really what's happening here. This associates a link with an alternative representation, not the RSS feed icon :)
Comment #21
wim leersI forgot to fix my own 4th point. Stupid!
Comment #22
wim leersThe existing change record https://www.drupal.org/node/2160069 has already been updated.
Comment #23
catchs/build/variables/ ?
Comment #26
hussainwebI hope this fixes some of the failures.
Comment #27
wim leersMy patch was flawed, as #23 shows. And that's precisely what fixed the test failures also. Hurray :)
Comment #28
catchI opened, reviewed and RTBCed this issue. But since Alex has reviewed it too now and that feedback has been dealt with, I think I can commit it as well without too much guilt.
Committed/pushed to 8.0.x, thanks!
Comment #30
tstoecklerUnless I am majorly missing something this patch is a huge regression for contrib due to
In Drupal 7 due to the
call_user_func()call, it was absolutely possible for contrib modules to participate in the#attachedprocess. You can - for example - attach external libraries to render arrays by using'#attached' => ['libraries_load' => [['foo']]]. I don't see how we could achieve the same with this patch.Edit: That's in D7 and with Libraries API.
Unless someone can show me how to achieve the same, this patch should either be rolled back or amended. I can't really tell whether the latter would be feasible or not.
Comment #31
ianthomas_ukDo we want to allow contrib to do that? I think the libraries example would now be covered by core, but are there other use cases? I can see us wanting to further refactor the code that handles #attached, which might be impossible if we keep support for arbitrary #attached functions.
If we do, we could fairly easily support that in
drupal_process_attachedby restoringcall_user_func_arrayinstead of throwing an exception. I'd still keep the rest of the switch statement so we can have the friendly ['#attached']['feed'] style syntax. Is it worth making modules register the functions that can be called via #attached, so people don't just attach any old function?Comment #32
catchYes that was part of the discussion where we said we should remove support.
Right now the _drupal_add_*() functions that get called in the end are horrible legacy static things that we want to refactor still.
IMO adding support for more methods is either of the following:
1. Core issues for anything missing (i.e. the 7.x use cases that should be handled in 8.x already now)
2. New major issue to expand support for contrib - but needs a defined use case to do so.
We can make API additions at any point during the 8.x cycle, but it's much less easy to remove support for side effects like the call_user_func() on any string call.
Leaving this open but if someone fancies opening either #1 and/or #2 that feels like the right way forward.
Comment #33
dawehnerIt sounds a bit like we need some basic registry of key -> callable for those kind of bit. Do you know of any other usecase in core?
Comment #34
xjmComment #35
tstoecklerCan you give some examples of this? I'm pretty certain that this is not the case, to be honest.
Sadly the results of this discussion did not make it into the issue summary. The quote I gave in #30 is the only reference of this.
Again, I fail to see how this is handled by 8.0.x.
The alternative to having Libraries API use
#attachedin some way, is to use a staticlibraries_load()call in Drupal 8 which is not something I would like to encourage.I think what would be sufficient already would be a mechanism for contrib to to have a mechanism (basically just some callable) which can process a more domain-specific structure into
#attached][jsand#attached][css. That would totally serve Libaries API's use-case.For example:
Not sure if that would make the implementation easier.
Comment #36
rjacobs commented@tstoeckler, it's good to see that you are already bringing the Libraries API case into the discussion, as that may be the most salient example from contrib of jarring ramifications (e.g. one of the D8 projects I'm working on now was already affected as a result of this change coupled with the dependency on Librareis API).
I think something similar to #35 (an intermediary function to alter the build array directly) also came up toward the end of an old Libraries API thread: #2183087: Find a way around _drupal_add_js/css. Could some part of that actually be handled by core, or would the onus shift to each contrib module (like Libraries API) to provide their own unique methods to append-to $build arrays? Maybe as catch noted that has to be a follow-up issue?
Comment #37
tstoeckler@rjacobs I don't see how something like this could be done in contrib, TBH. If there is a way, I'd gladly implement it in Libraries API. And the Libraries API code does not even have to be elegant. I just care for the DX of people using Libraries API. I.e. we could circumvent this problem by forcing people to add a #pre_render callback provided by Libraries API which would take care of the processing but I don't find that acceptable DX.
I personally would find it backwards to open a follow-up for resolving a regression, but I find a lot of things about our issue queue backwards, so as long as we find a solution, I'm happy.
Comment #38
rjacobs commentedYea, it looks like preserving the current DX for important cases like Libraries API would be impossible without additional core changes. My question about what contrib could do in reaction was more akin to your
#pre_rendercallback example, which I do agree is less than ideal (at least from my perspective as a contrib maintainer).One option would be to add-back the
call_user_func_array($callback, $args);option todrupal_process_attached()for thedefaultcase (in the switch). However, this would propagate the dependence on _drupal_add_* functions in the callbacks. Barring that, it looks like modules just need an opportunity to interact and alter $elements['#attached'] if certain "unknown" $callback strings are found. If this was an option I guess the related logic would have to fire quite early indrupal_process_attached()(in case module's added new js, css or library entries).Comment #39
dawehnerOne idea tobias and I had after a short conversation: Make the available things in
#attachedtagged services. With this contribmodules could extend the list of available things in there without having the requirement to expose every callable.
Does someone has any opinions about that?
Comment #40
berdirSounds interesting. Or just fixed service names? Like attached_processor.name_of_attached_key? We don't really need to have a list of them? And we need the container anyway to lazy-load those services. The pattern is maybe not too common, but I think people are kind of familar with specially-named services, like cache bins, even though that is not a requirement there.
When porting to this, I also noticed that html_head still requires that super-weird structure so that it can call call_user_func_array() on the value. That is extremely un-intuitive. As it is hardcoded now, I'm wondering if we want to make the DX there better (would need another API change of course, unless we want to keep a BC layer). drupal_add_html_head has that second argument as a unique identifier, so that could be the array key for example. The same would work for tagged services, afaik we already use the key for something (js settings or something?) so we would have to pass the key and value to those services anyway.
So instead of
You could write:
Comment #41
catchWas thinking of something like tagged services at DrupalCon, I'd be fine with that. However what the functions currently do and what they should be doing is pretty far apart, we might want to wait for #2352155: Remove HtmlFragment/HtmlPage which I believe from discussion with Wim puts us in a position to get rid of the reliance on statics.
What exactly is it that libraries API does in 8.x that can't be done with core library support?
Comment #42
berdirlibraries.module is about external libraries in the /libraries folder, it takes care of checking they existing and providing the correct actual path (it can be /libraries or /profiles/something/libraries for example). That is still needed for everything but core, which is in the special position of being allowed to just include external dependencies.
Some things that were done with libraries.module in 7.x can now be done with composer dependencies, but not every library out there is already a composer package and it doesn't help with assets yet.
One specific example where I am using it in 8.x is http://cgit.drupalcode.org/jw_player/tree/?h=8.x-1.x. The approach I chose there is to implement jw_player_libraries_info() for libraries.module and then based on that dynamically expand my "jw_player.libraries.yml" definition with jw_player_library_info_alter() (Yes, the hook names there are *that* confusing... :(). That then allows me to use #attached with library. As I didn't even occur to me that the callback thing is possible ;)
Comment #43
wim leersFirst of all, AFAIK Libraries API is the only example of a module needing to have things
#attached. Correct me if I'm wrong.@tstoeckler: the answer below is intended to be objective, and criticizes the D7 code and proposes a better way forward. Please don't take it personal.
This is a possibility.
But that brings me to the point of: what processing would it then do? Libraries API has one purpose: allowing multiple modules to use the same asset library that is not available in Drupal core. But in Drupal 7, Libraries API is utterly broken in several ways:
navbarmodule works around this by having its own hacky conversion: http://cgit.drupalcode.org/navbar/tree/navbar.module?id=503dae836bc9d772.... But there's more…drupal_add_css(), even though#attachedwas indeed already a soft requirement in D7, to ensure all necessary assets are loaded in case of cache hits:libraries_load($name);$element['#attached']['libraries_load'][] = array('myAwesomeLibrary');(Libraries API developer docs are at https://www.drupal.org/node/1342238)
Problem 1 must be solved in Drupal 8, since Drupal 8 much more strictly requires dependencies to be declared. That's how Drupal 8 succeeds in loading only the necessary JS.
Therefore, I think the answer to all of this can be quite simple: Libraries API is in the business of not providing a
libraries.info.ymlfile, since it doesn't come with a static list of available libraries. Instead, it is in the business of implementinghook_library_info_alter()and populating that with the libraries it provides. So that e.g.mymodule/some-librarycan declare a dependency onlibraries/fooorlibraries/reactorlibraries/angularor …Then there is no need for
$element['#attached']['libraries_load'] = …anymore, because the libraries Libraries API makes available will just be attachable like this:$element['#attached']['library'][] = 'libraries/foo';.This would make Libraries API fit in perfectly with the existing architecture. Note that this is also how it could (and IMO should) work in Drupal 7.
If problem 1 is to be solved, problems 2 and 3 go away automatically. Problems 4 and 5 are the real problem space for Libraries API to solve, both in 7 and 8, and beyond.
Is my proposal flawed, because I'm overlooking something?
Indeed, this is *extremely* bad DX. We should fix that. We can easily fix that. Very few people will be affected anyway.
Comment #44
danny englanderI am wondering if we can clarify the status of this issue? It's linked to in the release notes for drupal 8.0.0-beta2 but it does not looked fixed. Indeed, in my contib theme, I am now getting a
Call to undefined function drupal_add_html_head()message which results in a WSOD. It had previously worked fine in drupal 8.0.0-beta1.Comment #45
ianthomas_ukRE #44, see the change request linked in the right bar on this issue, https://www.drupal.org/node/2160069
The issue was fixed, which involved removing some functionality. It is still open while we work out how to handle contrib modules that relied on that functionality (which doesn't apply in many cases).
Comment #46
catchOpened #2358727: Figure out what if anything libraries API needs for #attached support, I'm not at all clear what's actually missing there. Moving this back to fixed.
@Danny Englander: drupal_add_html_head() won't be coming back, the change notice covers the changes you'll need to make in your theme.
Comment #47
tstoecklerSorry for taking a while to respond here, will follow up in the other issue now.
Comment #48
karolus commentedI've been coming across the same issues that Danny Englander has in post #44, and have posted my code here. Have been testing things, but so far, the only successful way I have found to add a meta tag to the head was by hacking DefaultHtmlFragmentRenderer.php in core, which I know is a bad idea.
Comment #49
danny englander@Karolus, I posted a question on Drupal Answers in regard to this. I've been trying to figure this out all morning but still no luck. I've even tried some of the examples from core using the new
hook_page_attachments(drupal 8 dev) as well ashook_page_build/hook_page_alterin combo with the new suggested method but still no luck afterdrush cr.Comment #50
karolus commented@Danny Englander:
Yes, found your question there earlier--it was pretty much the same thing I was trying. Going into more detail onto how I hacked core to get it to work (know this isn't the right way to do things):
Went to core/modules/system/src/Tests/System/DefaultHtmlFragmentRenderer.php and added this line below line 157:
$page->addMetaElement(new MetaElement(NULL, array('http-equiv' => 'X-UA-Compatible', 'content' => 'IE=edge,chrome=1')));In doing this, the meta tag worked as it should.
Comment #51
danny englander@karolus - I added a working example to the change notice. However, there are now two viewport tags in my case, one from core and the one added from my theme so I am not sure how to handle that.
Comment #53
delta commentedWhen I add this code in a
hook_views_pre_render($view)I got two tags
<title></title>in the<head>.One with the views page title, and the one I added in my hook.. The one I added in the hook views pre render is in first position.
Any thought ?
Comment #54
joelpittet@delta could you post a new issue and cross reference it here? This has been closed for some time so people aren't actively look for the issue you had.
Comment #55
delta commented@joelpittet Thanks, it's done https://www.drupal.org/node/2665378