Score:0

How to get rendered node according to active theme?

jp flag

In a hook_entity_postsave() function in my custom D7 module, I'm doing the following when a node is saved:

$_ = node_view($entity, 'full');   
$html = drupal_render($_);         

But the renderd HTML seems to miss the active theme's templates. e.g. turning on theme debug, I get

 FILE NAME SUGGESTIONS:
   * paragraphs-items--field-sections--full.tpl.php
   * paragraphs-items--field-sections.tpl.php
   x paragraphs-items.tpl.php

Yet paragraphs-items--field-sections--full.tpl.php is defined in the site's active theme (and is used correctly on presentation of normal node pages).

EDIT: I think this may be because the admin theme is active when this code runs? Is there a way to switch that?

Score:1
bd flag

It is definitely because your admin theme is active when you do the render.

There is no straight-forward and easy way to switch the theme in the middle of a request and you will likely run into further problems.

I would consider other options, e.g. creating the nodes markup via a menu callback that uses the frontend theme, either directly in your hook_entity_postsave or in a cron job for example.

If you still want to try it by switching themes, this is the way it worked for my now after a bit of tinkering with the code from https://stackoverflow.com/a/56358189/368479 and borrowing code from drupal_theme_initialize(). The most notable change is the clearing of the cache at the end of the switch function. Performance-wise this is awful, but I couldn't make it work without it.

/**
 * Switch to or from an alternative theme in the middle of a request.
 *
 * This is useful if you need to render something (like a node) in a different
 * theme without changing the theme of the entire page. An example use case is
 * when you need to render something for a front end user from an admin page.
 *
 * Usage example:
 *     my_module_switch_theme('bartik');
 *     $node = node_load(1);
 *     $renderable = node_view($node);
 *     $rendered = render($renderable);
 *     my_module_switch_theme();
 *
 * @param string|null $to
 *   The name of the theme to switch to. If NULL, it switches back to the
 *   original theme.
 */
function my_module_switch_theme(string $to = NULL) {
  global $theme, $theme_key;

  // Backup the original theme.
  static $original_theme;
  if (empty($original_theme)) {
    $original_theme = $theme;
  }

  // Perform the switch.
  $theme = $to ?? $original_theme;
  $theme_key = $theme;

  // Find all our ancestor themes and put them in an array.
  $themes = list_themes();
  $base_theme = array();
  $ancestor = $theme;
  while ($ancestor && isset($themes[$ancestor]->base_theme)) {
    $ancestor = $themes[$ancestor]->base_theme;
    $base_theme[] = $themes[$ancestor];
  }
  _drupal_theme_initialize($themes[$theme], array_reverse($base_theme));

  // Themes can have alter functions, so reset the drupal_alter() cache.
  drupal_static_reset('drupal_alter');

  // Clear the cache.
  drupal_flush_all_caches();
}

Note that this might not work if you implement hook_custom_theme for the page where the action happens, but if you use the admin theme then I guess that is not the case.

The code above can then be used like in the following example and correctly uses the node.tpl.php from the bartik theme instead of the one that the current theme uses, at least in my test setup.

my_module_switch_theme('bartik');
$node = node_load(1);
$renderable = node_view($node);
$rendered = render($renderable);
my_module_switch_theme();
jp flag
Thanks, I tried this but it doesn't work. I've also tried the fuller version at https://stackoverflow.com/a/20476484/623519 too. It's just not picking up the templates from the theme :-(
berliner avatar
bd flag
@artfulrobot You are right. Also there is no global `$custom_theme` variable. I was mislead by something I saw and didn't verify. I have updated my answer now. I would strongly suggest to do the rendering at a later point if that is possible. Switching the themes during the active request will easily lead to trouble.
jp flag
Thanks! it works! I have found a peformance gain: replace `drupal_flush_all_caches()` with `system_rebuild_theme_data();drupal_theme_rebuild();`
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.