If a hook_init() function calls a theme function (for instance, to supply some rendered HTML to a drupal_add_js() settings call on every page), and the user has set up a separate administration theme, they will be unable to change blocks for the non-admin theme (see #219756: blocks cannot be configure if administration theme used).

This is because calling the theme function from hook_init seems to set up some global $theme variables before they're ready, and they get set to the admin theme when they should get set to the normal theme.

Steps to reproduce:

1. Set an admin theme in admin/settings/admin
2. Enable a module that calls a theme function from hook_init(). The Teleport module does this by calling drupal_get_form().
3. Navigate to the block administration page at admin/build/block.

Expected behavior: The block admin page should render in the non-admin theme, and moving blocks around and saving should apply the save to the non-admin theme.

What happened instead: The block admin page renders in the admin theme, and when changes to blocks are saved, the changes are applied to the admin theme and do not show up on non-admin pages. This happens even though the secondary tab is titled with the name of the non-admin theme. This makes it impossible to change blocks for the non-admin theme.

Workaround: disable the offending module before modifying the blocks, and re-enable it when you're done.

Comments

dvessel’s picture

I think it's related to the $custom_theme global. Specifically, the one set inside system_init(). It has to run first but your module does instead and initializing the theme system before system_init() just breaks it. This is just a guesstimate and needs verification.

mfer’s picture

If the theme system is initialized too early it creates problems with $custom_theme and would create this problem. We can into this on the sections module... http://drupal.org/node/47788#comment-632662

hook_init is too early for the theme system to initialize cleanly.

In the case of the teleport module I'd suggest trying to find a different hook to add drupal_get_form. It might even be best to do it at the end instead of at the front. What about hook_exit?

dvessel’s picture

Version: 6.0-rc4 » 7.x-dev

Although $custom_theme from system_init is a problem, the problem specific to this issue is that your calling drupal_get_form() which initializes the theme system on drupal_render_form(). It's done way too early.

Look at block_admin_display, it sets $custom_theme then it calls _block_rehash > init_theme. This is the expected order but hook_init is done before block_admin_display and your use of drupal_get_form automatically initializes the theme system.

This is the order of the relevant functions on the block admin page:

a.) teleport_init
b.) drupal_get_form
c.) init_theme << done way too early
1.) system_init
2.) block_admin_display
2b.) set $custom_theme << needs to come first.
3.) init_theme << needs to come after.
4.) drupal_get_form
5.) block_admin_display_form

Trying to fix this is too late. The docs should really be updated for hook_init.

incidentist’s picture

Is this a sufficiently complex problem that it has to wait until 7.x? Could it get worked into 6.1 as a bugfix?

To be clear: it's not just a matter of drupal_get_form() -- calling any theme function from hook_init() triggers the same problem.

mfer’s picture

This is something that will have to wait for D7. It would require altering the workflow of drupal to change. This is too big of a change to do at a point release. And, this has been around for awhile. You will run into this same problem in D5.

sime’s picture

Those with same symptoms might also check for modules that call path_to_theme:
see -> http://drupal.org/node/178364

attiks’s picture

yang_yi_cn’s picture

is there any workaround?

lizbethalml’s picture

Hey yang_yi_cn you're alive!!! I've posted a lot of questions in your jcarousel_block module and have sent you a lot of messages... could you please please help me??? how can I resize my images inside the jcarousel_block??? sorry to post this here but you don't seem to check the other issues!!! thank's anyway

Damien Tournoud’s picture

Status: Active » Closed (works as designed)

This is not a bug. Calling theme functions in hook_init() is not supported. Don't do that.

snorkers’s picture

I'm having a problem, possibly related to this 'unsupported feature' where I cannot:

1. Currently change out of my D6 base theme (a sub theme of Blueprint) - even tried selecting Garland with no change made
2. If I use a different theme for admin, block changes are not implemented (I had been going in directly to the MySQL tables).

This all stemmed from trying out the Admin module and wondered why the Slate theme was not automatically appearing when looking at admin pages. Now I have a D6 install that has loads of contrib modules, so was wondering if there are pointers to look for in the various modules - I don't have huge understanding of theming so if there is any advice, please keep it simple and would welcome any pointers.

I may be looking in the wrong area... I'm also wondering why this is in the D7 queue for 18 months, and not really documented anywhere in D6? Should this not remain flagged as D6?

David_Rothstein’s picture

Status: Closed (works as designed) » Active

This certainly seems like a bug to me. You can't even safely call t() from within hook_init(), since that will indirectly initialize the theme system as well. Plus, I think there are a lot of legitimate reasons for wanting to access the theme system early on.

I have a patch at #553944: Define hook_menu_get_item_alter() as a reliable hook that runs before the page is doomed which is really more of a feature request, but one of the side effects is that it would fix this bug (in fact, I started writing the patch in an attempt to fix this bug, since we were running up against in D7 in a lot of ways).

If that gets in, we could close this, but otherwise I think this should stay open in case someone can come up with a simpler fix (especially one that could be backported to D6). It's possible you could partly fix the issue by moving block.module's code into block_init() - rather than the page callback where it is now - but that would be kind of ugly too.

mattyoung’s picture

Calling theme functions in hook_init() will screw up maintenance page also. It will turn off the template_preprocess_maintenance_page() function because the $theme global is set, shutting off _drupal_maintenance_theme().

The result of this is blank look maintenance page because none of the expected variables are set.

I was told this is expected, not a bug.

David_Rothstein’s picture

Version: 7.x-dev » 6.x-dev

This issue is now fixed in Drupal 7 due to #553944: Define hook_menu_get_item_alter() as a reliable hook that runs before the page is doomed.

It's still a bug in Drupal 6, though. The fix that went into Drupal 7 isn't something that can be backported, but leaving this open because maybe there's a more targeted solution that someone will be able to find for Drupal 6.

bendev’s picture

subscribe

AlexisWilke’s picture

I ran in this problem with the simplemenu project. Then something came to my mind as a potential fix for the Blocks & System modules.

Assuming that the teleport_init() function can be called later (i.e. by setting the weight of the module to 1 or more) and that the block could have a block_init() function that sets up the $custom_theme variable and that the system_init() does not overwrite the custom theme variable if already set, then we can make it work (teleport_init() is just an example, simplemenu_init() has the same issue and I tested with that module instead.)

So, the result would be something like this:

  1. block_init() [weight 0]
  2. system_init() [weight 0]
  3. teleport_init()/simplemenu_init() [weight 1]

The theme for the block list is set in block_init() and not in block_admin_display(). block_init() could look like this:

function block_init() {
  global $custom_theme;

  if (arg(0) == 'admin' && arg(1) == 'build' && arg(2) == 'block') {
    if (is_null(arg(3)) || arg(3) == 'list') {
      // If non-default theme configuration has been selected, set the custom theme.
      $theme = arg(4);
      $custom_theme = $theme ? $theme : variable_get('theme_default', 'garland');
    }
  }
}

That same Block code would be removed from the block_admin_display() function, of course.

function block_admin_display($theme = NULL) {
  // code here removed

  // Fetch and sort blocks
  $blocks = _block_rehash();
  usort($blocks, '_block_compare');

  return drupal_get_form('block_admin_display_form', $blocks, $theme);
}

The system_init() function needs a small tweak to avoid setting custom theme (and overwriting the block setup):

function system_init() {
  global $custom_theme; // the custom theme may already be set when we reach this function

  // Use the administrative theme if the user is looking at a page in the admin/* path.
  if (!$custom_theme && (arg(0) == 'admin' || (variable_get('node_admin_theme', '0') && arg(0) == 'node' && (arg(1) == 'add' || arg(2) == 'edit')))) {
    $custom_theme = variable_get('admin_theme', '0');
    drupal_add_css(drupal_get_path('module', 'system') .'/admin.css', 'module');
  }

  ...

Since at this time there is no block_init() I would imagine that would not cause a problem. The system_init() function will happen after so we do not need to change weights.

Thank you.
Alexis

P.S. I tested this and it works great with simplemenu!

Alan D.’s picture

I haven't used the code suggested by Alexis, but the logic seems to work. Here was the cut 'n paste into a custom module that resolved the issue (probably with some performance issues). Anyway, good enough for the project that we are working on:

<?php
function HOOK_init() {
  // The hook_init() is too early for building the theme - this is a work around
  if (arg(0) == 'admin' && arg(1) == 'build' && arg(2) == 'block') {
    if (is_null(arg(3)) || arg(3) == 'list') {
      // If non-default theme configuration has been selected, set the custom theme.
      $theme = arg(4);
      $custom_theme = $theme ? $theme : variable_get('theme_default', 'garland');
    }
  }
  // Use the administrative theme if the user is looking at a page in the admin/* path.
  if (!$custom_theme && (arg(0) == 'admin' || (variable_get('node_admin_theme', '0') && arg(0) == 'node' && (arg(1) == 'add' || arg(2) == 'edit')))) {
    $custom_theme = variable_get('admin_theme', '0');
    drupal_add_css(drupal_get_path('module', 'system') .'/admin.css', 'module');
  }

  // Hopefully safe to call this now.
  init_theme();
  ...
}
?>
bleen’s picture

AlexisWilke’s picture

bleen18,

You have both problems then. The hook_init() is different from the Input filters and also interfere with the administration theme.

Note that in the simplemenu module I actually changed the code so the theme() function is not called by default. Solved many problems.

Thank you.
Alexis Wilke

bleen’s picture

re #20: Yeah ... makes me sad. As a stop-gap/bandaid I simply added a call to system_init() to the beginning of strongarm_init().

Not the prettiest solution, but its simple and works well.

rjb’s picture

Another way to circumvent HOOK_init() is this:

function HOOK_theme_registry_alter(&$theme_registry) {
  array_unshift($theme_registry['page']['preprocess functions'], 'your_hook_init_function');
}

This way you can safely call theme() functions from your init function without any problems.
I found this solution at http://drupal.org/node/599106 and it really works.

Cheers,
Ronald

ShaneOnABike’s picture

Hrm I also seem to have this problem. The weird thing is that it is happening only when I do a form submit. I can't figure out a way in which to hook into the admin forms to deal with this properly.

One solution I came up with was to setup a stub module with the init but I'm wondering what weight modules were set at to solve this?

Status: Active » Closed (outdated)

Automatically closed because Drupal 6 is no longer supported. If the issue verifiably applies to later versions, please reopen with details and update the version.