Skip to main content

Changing the Drupal theme based on path

Published on

When using Drupal we all know there are two themes: default and administrative. By having two different themes site managers have a better user experience by knowing "this is public," and "this is administrative." It also proves beneficial to have specific administration themes due to the kinds of content and forms that a site manager has to interface with. Drupal does provide the option of letting site builders decide if adding or editing a node takes place in the default theme or the administrative theme.

Chances are, at least in all of my cases, you have "Use the administrative theme when editing or creating content" checked. There's just one drawback: what if one specific node (or other entity for that matter) should be edited through the default theme?

A recent project utilized a simplistic forum, so I enabled Drupal core's Forum module. Forum is built on nodes and terms, which means editing and creating forum nodes will fall within the administrative theme. Regular users may not have "View the administration theme" permission, but what about site administrators? Just because a user has elevated permissions does not mean his or her user experience should have deteriorated. Luckily Drupal's menu subsystem defines the theme used per path. Using hook_menu_get_item_alter() there is a way to modify the theme callback and change a path's theme.

/**
 * Implements hook_menu_get_item_alter().
 */
function mymodule_menu_get_item_alter(&$router_item, $path, $original_map) {
  if ($router_item['path'] == 'node/add/forum') {
    $router_item['theme_callback'] = 'mymodule_menu_theme_callback'; 
  } 
} 

function mymodule_menu_theme_callback() { return 'default_theme'; }

I do want to note that there is hook_custom_theme(). For those who haven't heard of it, you've been saved some heartache. For those who have, I feel your pain. Inspecting this snippet from menu.inc you can see hook_custom_theme() is always overridden if menu_get_item() returns an actual callback function.

    /**
     * Excerpt from menu.inc
     * Lines 1745-1755
     */
  
    // First allow modules to dynamically set a custom theme for the current
    // page. Since we can only have one, the last module to return a valid
    // theme takes precedence.
    $custom_themes = array_filter(module_invoke_all('custom_theme'), 'drupal_theme_access');
    if (!empty($custom_themes)) {
      $custom_theme = array_pop($custom_themes);
    }
    // If there is a theme callback function for the current page, execute it.
    // If this returns a valid theme, it will override any theme that was set
    // by a hook_custom_theme() implementation above.
    $router_item = menu_get_item();

The proofs in the code, which makes me wonder why it is even there. Regardless using hook_custom_theme() over hook_menu_get_item_alter() provides more reliable results

I'm available for one-on-one consulting calls – click here to book a meeting with me 🗓️