Drupal允许为管理后台设置独立的theme,保存在系统变量variable_get('admin_theme')。
Drupal使用全局变量$theme来保存当前请求对应的主题。Drupal在启动时初始化$theme变量:
function _drupal_bootstrap_full() { ... ... menu_set_custom_theme(); drupal_theme_initialize(); module_invoke_all('init'); } function menu_set_custom_theme() { menu_get_custom_theme(TRUE); } function menu_get_custom_theme($initialize = FALSE) { $custom_theme = &drupal_static(__FUNCTION__); // Skip this if the site is offline or being installed or updated, since the // menu system may not be correctly initialized then. if ($initialize && !_menu_site_is_offline(TRUE) && (!defined('MAINTENANCE_MODE') || (MAINTENANCE_MODE != 'update' && MAINTENANCE_MODE != 'install'))) { // 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. // 调用hook_custom_theme()获取自定义theme $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. // 在hook_menu()定义时也可以用theme callback/theme arguments定义theme $router_item = menu_get_item(); if (!empty($router_item['access']) && !empty($router_item['theme_callback']) && function_exists($router_item['theme_callback'])) { $theme_name = call_user_func_array($router_item['theme_callback'], $router_item['theme_arguments']); if (drupal_theme_access($theme_name)) { $custom_theme = $theme_name; } } } return $custom_theme; } function drupal_theme_initialize() { global $theme, $user, $theme_key; // If $theme is already set, assume the others are set, too, and do nothing if (isset($theme)) { return; } drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE); $themes = list_themes(); // Only select the user selected theme if it is available in the // list of themes that can be accessed. // 检查有没有设置用户主题? $theme = !empty($user->theme) && drupal_theme_access($user->theme) ? $user->theme : variable_get('theme_default', 'bartik'); // Allow modules to override the theme. Validation has already been performed // inside menu_get_custom_theme(), so we do not need to check it again here. // 检查有没有设置自定义主题? $custom_theme = menu_get_custom_theme(); $theme = !empty($custom_theme) ? $custom_theme : $theme; ... ... }
system模块实现了hook_custom_theme()钩子,检查当前请求menu是否属于admin:
function system_custom_theme() { if (user_access('view the administration theme') && path_is_admin(current_path())) { return variable_get('admin_theme'); } } function path_is_admin($path) { $path_map = &drupal_static(__FUNCTION__); if (!isset($path_map['admin'][$path])) { $patterns = path_get_admin_paths(); $path_map['admin'][$path] = drupal_match_path($path, $patterns['admin']); $path_map['non_admin'][$path] = drupal_match_path($path, $patterns['non_admin']); } return $path_map['admin'][$path] && !$path_map['non_admin'][$path]; }
system_custom_theme()调用path_is_admin()检查请求是否属于admin。path_is_admin()再调用path_get_admin_paths()返回哪些请求是admin,哪些请求是non_admin。
function path_get_admin_paths() { $patterns = &drupal_static(__FUNCTION__); if (!isset($patterns)) { $paths = module_invoke_all('admin_paths'); drupal_alter('admin_paths', $paths); // Combine all admin paths into one array, and likewise for non-admin paths, // for easier handling. $patterns = array(); $patterns['admin'] = array(); $patterns['non_admin'] = array(); foreach ($paths as $path => $enabled) { if ($enabled) { $patterns['admin'][] = $path; } else { $patterns['non_admin'][] = $path; } } $patterns['admin'] = implode(" ", $patterns['admin']); $patterns['non_admin'] = implode(" ", $patterns['non_admin']); } return $patterns; }
path_get_admin_paths()如何判断请求是admin还是non_admin?这又要调用另外一个钩子hook_admin_paths()。system模块就实现了这个钩子:
function system_admin_paths() { $paths = array( 'admin' => TRUE, 'admin/*' => TRUE, 'batch' => TRUE, // This page should not be treated as administrative since it outputs its // own content (outside of any administration theme). 'admin/reports/status/php' => FALSE, ); return $paths; }
总结一下:
1. system模块通过实现hook_admin_paths()钩子定义了所有以admin/开头的请求都属于admin请求。
2. system模块通过实现hook_custom_theme()钩子定义了所有admin请求都使用admin_theme。