In Google Analytics and many other statistics modules we need to process some logic inside hook_footer() as it was the very last (latest) hook we had in past. Now hook_footer() has died for any reasons I don't know. If I remember correctly docs told me I should use hook_page_alter() as an replacement of hook_footer. Ok - I've done this, but now the inline code is not added to the footer ($page_bottom) any longer.
You can look into CVS of Google Analytics or take the latest DEV for testing. Below code does not add the inline code to the footer.
function googleanalytics_page_alter(&$page) {
//... collect some data (like 404 headers) I can only get in hook_footer() as it's the very last ...
// Add inline code to the footer ('page_bottom' region).
drupal_add_js($script, array('type' => 'inline', 'scope' => 'footer'));
}
Now I'm not able to implement Google Analytics module and this is really one of the top 10 most popular modules on d.o with over 90.000 installations.
How can we solve this issue?
Comments
Comment #1
gábor hojtsyThe use of hook_page_alter() dictates you add your stuff directly to the $page array. Also, since Drupal 7 has #attached with css and js keys. See http://drupal.org/node/224333#attached_js Hook_footer()'s removal specifically is explained in http://drupal.org/node/224333#hook_footer (hook_footer() was removed, $closure became $page_bottom, $page_top added)
Comment #2
hass commentedCould you give me an example, please? $closure does not only became $page_bottom - the logics also changed heavily. I do not have a form.
Comment #3
hass commentedAwful, but seems to work and defines *twice* the scope with
$page['footer']and'scope' => 'footer'.And this is what I need much more - but it doesn't work at all:
Comment #4
hass commentedComment #5
hass commentedAdding something to header this way doesn't work, too. Nevertheless the $page['header'] array looks 100% same like $page['footer']
but this works
works... this is not really a clean API. Why the heck should I add something to
$page['page_bottom']with'scope' => 'header'. This is such illogical! And I'm totally unable to add the JS code to 'page_top', but documented that I'm able to do apply JS to custom regions and not only to header and footer scope.Comment #6
damien tournoud commentedThe key you are missing to understand what happens here is that '#attached' are merely functions executed when a part of the page is rendered.
It *doesn't matter* where you put your #attached javascript, it will *always* got either in the specified scope. Even this will work:
Do not confuse this issue with the other one you opened about scope and regions. They are not related at all.
Comment #7
hass commented@Damien: This issue was opened for the reason that I wondered that drupal_add_js is not working inside hook_page_alter(). Ok - Gabor explained this is not the way it was designed. OK - I tried myself to understand the hard way as there seems not so much documenation yet and understanding is not that easy! I'm sorry that I have not followed D7 development for a very few months. We could change this into a support request until we figured out if there are bugs or not... This case is not a duplicate of the other.
Additionally I'm sorry to say you may theoretically be correct, but then I guess I've found a few bugs... see below:
This does NOT add the inline code to the scope footer. Now - is this a bug or is this broken by design?
Attaching JS inline code to scope 'header' and 'footer' in the regions and scopes works if they use
<?php print render($page[region]) ?>in page.tpl.php. Something seems to be different if it comes to<?php print $page_top ?>if they are inside html.tpl.php as this variables do not require to be surrounded by render() to attach the JS to the appropiate scope. But using$page['head']['googleanalytics']['#attached']['js']is still not working for me in $head - with and without render() - there is nothing attached. If this is not confusing I do not know... don't get me wrong - I may not understand the deep insides as I'm only analyzing...Overall I cannot say if all themes MUST use render() around all variables in page.tpl.php. If only one theme developer missed this, a module is not able to attach JS code. In past we only had variables and the theme upgrade page still does not tell developers to use render() in page.tpl.php. In many places it's stated I should use the old fashioned "variables", but they are not working anymore. It took me hours to figure out why my theme is not working and I only got the idea from Garland code.
Comment #8
hass commented@Gabor: You said in #1 hook_page_alter() dictates you add your stuff directly to the $page array. But why the heck is drupal_add_js working with "files" and "settings", but not inline JS? Only try the latest GA dev.
Comment #9
EvanDonovan commentedChanging from "critical" to "major," since this should probably not block release, though it is very confusing.
Neither of the linked examples from #1 explain how to handle hass' specific issue. The first example only explains how #attached works with forms, which seems to be different than how it works with hook_page_alter() since hass reports that it didn't work to do #attached for inline JS in the footer.
The second example only shows how to add markup to the footer, not JS specifically.
Honestly, I think that the best documentation that could be made would be for someone who understands this change to recommend how Google Analytics and also, possibly, GMap module should attach their Javascript, and add that documentation to the upgrade docs as a separate item (called "adding third-party JS in Drupal 7" or something similar). They are the two most widely-used modules which would have this issue, I think.
Comment #10
EvanDonovan commentedSorry, missed something - hass says in #7 that render() is required in page.tpl.php for hook_page_alter() to work properly. I thought that in the issue that added render() it was decided against requiring its use. I can see this being a pitfall for many (most?) themers, who are used to simply doing
print $variable. Perhaps this should be critical after all.Comment #11
effulgentsia commentedIs the original issue still happening? You should be able to call drupal_add_js() to add things to header or footer scope from anywhere that runs before template_process_html(), which is pretty much anywhere, including hook_page_alter().
#attached can be used instead of drupal_add_js() if the call to drupal_add_js() needs to be conditional on something being rendered. For example, if you only want to have drupal_add_js() called if a particular region/block/node/whatever is rendered, then the right way to do that is to add #attached to that element. Probably GoogleAnalytics code doesn't fall into this category. In any case, #attached is simply a wrapper to drupal_add_*() that only runs when the element is being rendered.
With regards to whether a template should
print render($variable);orprint $variable;, it depends on if $variable is a render array or not. The docs of each tpl.php file tries to make this clear. For example, in modules/node/node.tpl.php (hopefully) make it clear that it'sprint $title;andprint render($content);. If it doesn't, please open a separate issue with suggestions for improvement.Please re-open the issue if there's anything else to be done here.
Comment #13
hass commented@effulgentsia: Can you say where this bug has been fixed? In my testing it was not possible to use drupal_add_js() inside hook_page_alter() and now it should work.