Index: includes/theme.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/theme.inc,v retrieving revision 1.568 diff -u -p -r1.568 theme.inc --- includes/theme.inc 10 Jan 2010 02:15:12 -0000 1.568 +++ includes/theme.inc 10 Jan 2010 22:14:52 -0000 @@ -719,23 +719,18 @@ function list_themes($refresh = FALSE) { * need to be fast, and calling the non-hook-specific preprocess and process * functions for them would incur a noticeable performance penalty. * - * For template-implemented theme hooks, there are two special variables that - * these preprocess and process functions can set: - * 'template_file' and 'template_files'. These will be merged together - * to form a list of 'suggested' alternate template files to use, in - * reverse order of priority. template_file will always be a higher - * priority than items in template_files. theme() will then look for these - * files, one at a time, and use the first one that exists. If none exists, - * theme() will use the original registered file for the theme hook. - * - * For function-implemented theme hooks, there are two special variables that - * these preprocess and process functions can set: - * 'theme_function' and 'theme_functions'. These will be merged together - * to form a list of 'suggested' alternate functions to use, in - * reverse order of priority. theme_function will always be a higher - * priority than items in theme_functions. theme() will then call the - * highest priority function that exists. If none exists, theme() will call - * the original registered function for the theme hook. + * There are two special variables that these preprocess and process functions + * can set: + * 'theme_hook_suggestion' and 'theme_hook_suggestions'. These will be merged + * together to form a list of 'suggested' alternate hooks to use, in + * reverse order of priority. theme_hook_suggestion will always be a higher + * priority than items in theme_hook_suggestions. theme() will use the + * highest priority implementation that exists. If none exists, theme() will + * use the implementation for the theme hook it was called with. These + * suggestions are similar to and are used for similar reasons as calling + * theme() with an array as the $hook parameter (see below). The difference + * is whether the suggestions are determined by the code that calls theme() or + * by a preprocess or process function. * * @param $hook * The name of the theme hook to call. If the name contains a @@ -840,10 +835,9 @@ function theme($hook, $variables = array } // Invoke the variable processors, if any. The processors may specify - // alternate suggestions for which function/template should be used. + // alternate suggestions for which hook's template/function to use. if (isset($info['preprocess functions']) || isset($info['process functions'])) { - $variables['theme_functions'] = array(); - $variables['template_files'] = array(); + $variables['theme_hook_suggestions'] = array(); foreach (array('preprocess functions', 'process functions') as $phase) { if (!empty($info[$phase])) { foreach ($info[$phase] as $processor_function) { @@ -855,21 +849,28 @@ function theme($hook, $variables = array } } } - // Function suggestion takes priority over template suggestion. - // theme_function takes priority over theme_functions. - // theme_functions are in FILO order (least appropriate to most appropriate). - // Here, just look for function suggestions. Deal with template - // suggestions only after determining that the theme call is a template. + // If the preprocess/process functions specified hook suggestions, and the + // suggestion exists in the theme registry, use it instead of the hook that + // theme() was called with. This allows the preprocess/process step to + // route to a more specific theme hook. For example, a function may call + // theme('node', ...), but a preprocess function can add 'node__article' as + // a suggestion, enabling a theme to have an alternate template file for + // article nodes. Suggestions are checked in the following order: + // - The 'theme_hook_suggestion' variable is checked first. It overrides + // all others. + // - The 'theme_hook_suggestions' variable is checked in FILO order, so the + // last suggestion added to the array takes precedence over suggestions + // added earlier. $suggestions = array(); - if (!empty($variables['theme_functions'])) { - $suggestions = $variables['theme_functions']; + if (!empty($variables['theme_hook_suggestions'])) { + $suggestions = $variables['theme_hook_suggestions']; } - if (!empty($variables['theme_function'])) { - $suggestions[] = $variables['theme_function']; + if (!empty($variables['theme_hook_suggestion'])) { + $suggestions[] = $variables['theme_hook_suggestion']; } foreach (array_reverse($suggestions) as $suggestion) { - if (function_exists($suggestion)) { - $info['function'] = $suggestion; + if (isset($hooks[$suggestion])) { + $info = $hooks[$suggestion]; break; } } @@ -886,11 +887,9 @@ function theme($hook, $variables = array $render_function = 'theme_render_template'; $extension = '.tpl.php'; - // Run through the theme engine variables, if necessary + // The theme engine may use a different extension and a different renderer. global $theme_engine; if (isset($theme_engine)) { - // If theme or theme engine is implementing this, it may have - // a different extension and a different renderer. if ($info['type'] != 'module') { if (function_exists($theme_engine . '_render_template')) { $render_function = $theme_engine . '_render_template'; @@ -902,29 +901,11 @@ function theme($hook, $variables = array } } - // Find which template file exists and can be used. Priority order is: - // 1. $variables['template_file']. - // 2. $variables['template_files'] in FILO order (later in array is higher - // priority). - // 3. $info['template']. - $suggestions = array(); - if (isset($variables['template_files'])) { - $suggestions = $variables['template_files']; - } - if (isset($variables['template_file'])) { - $suggestions[] = $variables['template_file']; - } - if ($suggestions) { - $template_file = drupal_discover_template($info['theme paths'], $suggestions, $extension); + // Render the output using the template file. + $template_file = $info['template'] . $extension; + if (isset($info['path'])) { + $template_file = $info['path'] . '/' . $template_file; } - if (empty($template_file)) { - $template_file = $info['template'] . $extension; - if (isset($info['path'])) { - $template_file = $info['path'] . '/' . $template_file; - } - } - - // Render the output using the found template file. $output = $render_function($template_file, $variables); } @@ -934,53 +915,6 @@ function theme($hook, $variables = array } /** - * Determine and return which template file will generate the output. - * - * This helper allows the theme system to pick the template at runtime instead - * of build time. - * - * @see template_page_suggestions() - * @see template_preprocess_block() - * - * @param $paths - * The paths where templates can be found. See _theme_process_registry() - * 'theme paths' for more information. - * @param $suggestions - * The possible template names. These are derived from - * $variables['template_files'] and $variables['template_file], defined by - * preprocess functions. Each file is checked on every path in the order of - * precedence defined by theme(). - * @return - * The filepath to the template that will generate the output. If none is - * found, then theme() will use the 'template' as set by - * _theme_process_registry(). - * - * @see _theme_process_registry() - * @see theme() - */ -function drupal_discover_template($paths, $suggestions, $extension = '.tpl.php') { - global $theme_engine; - - // Remove slashes or null to prevent files from being included from - // an unexpected location (especially on Windows servers). - $extension = str_replace(array("/", "\\", "\0"), '', $extension); - - // Loop through all paths and suggestions in FIFO order. - $suggestions = array_reverse($suggestions); - $paths = array_reverse($paths); - foreach ($suggestions as $suggestion) { - if (!empty($suggestion)) { - $suggestion = str_replace(array("/", "\\", "\0"), '', $suggestion); - foreach ($paths as $path) { - if (file_exists($file = $path . '/' . $suggestion . $extension)) { - return $file; - } - } - } - } -} - -/** * Return the path to the current themed element. * * It can point to the active theme or the module handling a themed implementation. @@ -1253,8 +1187,7 @@ function theme_get_setting($setting_name * Render a system default template, which is essentially a PHP template. * * @param $template_file - * The filename of the template to render. Note that this will overwrite - * anything stored in $variables['template_file'] if using a variable processor hook. + * The filename of the template to render. * @param $variables * A keyed array of variables that will appear in the output. * @@ -2346,7 +2279,7 @@ function template_preprocess_html(&$vari } // Populate the body classes. - if ($suggestions = template_page_suggestions(arg(), 'page')) { + if ($suggestions = theme_get_suggestions(arg(), 'page', '-')) { foreach ($suggestions as $suggestion) { if ($suggestion != 'page-front') { // Add current suggestion to page classes to make it possible to theme @@ -2390,8 +2323,8 @@ function template_preprocess_html(&$vari $variables['head_title'] = implode(' | ', $head_title); // Populate the page template suggestions. - if ($suggestions = template_page_suggestions(arg(), 'html')) { - $variables['template_files'] = $suggestions; + if ($suggestions = theme_get_suggestions(arg(), 'html')) { + $variables['theme_hook_suggestions'] = $suggestions; } } @@ -2452,8 +2385,8 @@ function template_preprocess_page(&$vari } // Populate the page template suggestions. - if ($suggestions = template_page_suggestions(arg(), 'page')) { - $variables['template_files'] = $suggestions; + if ($suggestions = theme_get_suggestions(arg(), 'page')) { + $variables['theme_hook_suggestions'] = $suggestions; } } /** @@ -2481,26 +2414,39 @@ function template_process_html(&$variabl } /** - * Generate an array of page template suggestions. + * Generate an array of suggestions from path arguments. + * + * This is typically called for adding to the 'theme_hook_suggestions' or + * 'classes_array' variables from within preprocess functions, when wanting to + * base the additional suggestions on the path of the current page. * * @param $args * An array of path arguments, such as from function arg(). + * @param $base + * A string identifying the base 'thing' from which more specific suggestions + * are derived. For example, 'page' or 'html'. + * @param $delimiter + * The string used to delimit increasingly specific information. The default + * of '__' is appropriate for theme hook suggestions. '-' is appropriate for + * extra classes. * * @return - * An array of suggested template files. + * An array of suggestions, suitable for adding to + * $variables['theme_hook_suggestions'] within a preprocess function or to + * $variables['classes_array'] if the suggestions represent extra CSS classes. */ -function template_page_suggestions($args, $suggestion) { +function theme_get_suggestions($args, $base, $delimiter = '__') { - // Build a list of suggested template files and body classes in order of + // Build a list of suggested theme hooks or body classes in order of // specificity. One suggestion is made for every element of the current path, // though numeric elements are not carried to subsequent suggestions. For - // example, http://www.example.com/node/1/edit would result in the following - // suggestions and body classes: + // example, for $base='page', http://www.example.com/node/1/edit would result + // in the following suggestions and body classes: // - // page-node-edit.tpl.php page-node-edit - // page-node-1.tpl.php page-node-1 - // page-node.tpl.php page-node - // page.tpl.php + // page__node page-node + // page__node__% page-node-% + // page__node__1 page-node-1 + // page__node__edit page-node-edit $suggestions = array(); foreach ($args as $arg) { @@ -2509,15 +2455,15 @@ function template_page_suggestions($args // The percent acts as a wildcard for numeric arguments since // asterisks are not valid filename characters on many filesystems. if (is_numeric($arg)) { - $suggestions[] = $suggestion . '-%'; + $suggestions[] = $base . $delimiter . '%'; } - $suggestions[] = $suggestion . '-' . $arg; + $suggestions[] = $base . $delimiter . $arg; if (!is_numeric($arg)) { - $suggestion .= '-' . $arg; + $base .= $delimiter . $arg; } } if (drupal_is_front_page()) { - $suggestions[] = $suggestion . '-front'; + $suggestions[] = $base . $delimiter . 'front'; } return $suggestions; @@ -2529,7 +2475,7 @@ function template_page_suggestions($args * invoked. It is also used in theme_install_page() and theme_update_page() to * keep all the variables consistent. * - * An alternate template file of "maintenance-page-offline.tpl.php" can be + * An alternate template file of "maintenance-page--offline.tpl.php" can be * used when the database is offline to hide errors and completely replace the * content. * @@ -2617,7 +2563,7 @@ function template_preprocess_maintenance // Dead databases will show error messages so supplying this template will // allow themers to override the page and the content completely. if (isset($variables['db_is_active']) && !$variables['db_is_active']) { - $variables['template_file'] = 'maintenance-page-offline'; + $variables['theme_hook_suggestion'] = 'maintenance_page__offline'; } } @@ -2638,5 +2584,5 @@ function template_preprocess_region(&$va $region = drupal_region_class($variables['region']); $variables['classes_array'][] = $region; - $variables['template_files'][] = $region; + $variables['theme_hook_suggestions'][] = 'region__' . $region; } Index: modules/block/block.module =================================================================== RCS file: /cvs/drupal/drupal/modules/block/block.module,v retrieving revision 1.406 diff -u -p -r1.406 block.module --- modules/block/block.module 8 Jan 2010 11:10:32 -0000 1.406 +++ modules/block/block.module 10 Jan 2010 22:14:53 -0000 @@ -871,9 +871,9 @@ function template_preprocess_block(&$var $variables['classes_array'][] = drupal_html_class('block-' . $variables['block']->module); - $variables['template_files'][] = 'block-' . $variables['block']->region; - $variables['template_files'][] = 'block-' . $variables['block']->module; - $variables['template_files'][] = 'block-' . $variables['block']->module . '-' . $variables['block']->delta; + $variables['theme_hook_suggestions'][] = 'block__' . $variables['block']->region; + $variables['theme_hook_suggestions'][] = 'block__' . $variables['block']->module; + $variables['theme_hook_suggestions'][] = 'block__' . $variables['block']->module . '__' . $variables['block']->delta; } /** Index: modules/comment/comment.module =================================================================== RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v retrieving revision 1.829 diff -u -p -r1.829 comment.module --- modules/comment/comment.module 10 Jan 2010 00:41:28 -0000 1.829 +++ modules/comment/comment.module 10 Jan 2010 22:14:53 -0000 @@ -2124,7 +2124,7 @@ function template_preprocess_comment(&$v // Preprocess fields. field_attach_preprocess('comment', $comment, $variables['elements'], $variables); - $variables['template_files'][] = 'comment-' . $variables['node']->type; + $variables['theme_hook_suggestions'][] = 'comment__' . $variables['node']->type; // Helpful $content variable for templates. foreach (element_children($variables['elements']) as $key) { @@ -2215,7 +2215,7 @@ function template_preprocess_comment_wra // Provide contextual information. $variables['node'] = $variables['content']['#node']; $variables['display_mode'] = variable_get('comment_default_mode_' . $variables['node']->type, COMMENT_MODE_THREADED); - $variables['template_files'][] = 'comment-wrapper-' . $variables['node']->type; + $variables['theme_hook_suggestions'][] = 'comment_wrapper__' . $variables['node']->type; } /** Index: modules/field/field.module =================================================================== RCS file: /cvs/drupal/drupal/modules/field/field.module,v retrieving revision 1.60 diff -u -p -r1.60 field.module --- modules/field/field.module 4 Jan 2010 21:31:52 -0000 1.60 +++ modules/field/field.module 10 Jan 2010 22:14:53 -0000 @@ -777,11 +777,11 @@ function template_preprocess_field(&$var 'field-type-' . $field_type_css, 'field-label-' . $element['#label_display'], ), - 'template_files' => array( + 'theme_hook_suggestions' => array( 'field', - 'field-' . $element['#field_name'], - 'field-' . $element['#bundle'], - 'field-' . $element['#field_name'] . '-' . $element['#bundle'], + 'field__' . $element['#field_name'], + 'field__' . $element['#bundle'], + 'field__' . $element['#field_name'] . '__' . $element['#bundle'], ), ); $variables = array_merge($variables, $additions); Index: modules/forum/forum.module =================================================================== RCS file: /cvs/drupal/drupal/modules/forum/forum.module,v retrieving revision 1.550 diff -u -p -r1.550 forum.module --- modules/forum/forum.module 9 Jan 2010 23:09:51 -0000 1.550 +++ modules/forum/forum.module 10 Jan 2010 22:14:53 -0000 @@ -948,17 +948,17 @@ function template_preprocess_forums(&$va // Provide separate template suggestions based on what's being output. Topic id is also accounted for. // Check both variables to be safe then the inverse. Forums with topic ID's take precedence. if ($variables['forums'] && !$variables['topics']) { - $variables['template_files'][] = 'forums-containers'; - $variables['template_files'][] = 'forums-' . $variables['tid']; - $variables['template_files'][] = 'forums-containers-' . $variables['tid']; + $variables['theme_hook_suggestions'][] = 'forums__containers'; + $variables['theme_hook_suggestions'][] = 'forums__' . $variables['tid']; + $variables['theme_hook_suggestions'][] = 'forums__containers__' . $variables['tid']; } elseif (!$variables['forums'] && $variables['topics']) { - $variables['template_files'][] = 'forums-topics'; - $variables['template_files'][] = 'forums-' . $variables['tid']; - $variables['template_files'][] = 'forums-topics-' . $variables['tid']; + $variables['theme_hook_suggestions'][] = 'forums__topics'; + $variables['theme_hook_suggestions'][] = 'forums__' . $variables['tid']; + $variables['theme_hook_suggestions'][] = 'forums__topics__' . $variables['tid']; } else { - $variables['template_files'][] = 'forums-' . $variables['tid']; + $variables['theme_hook_suggestions'][] = 'forums__' . $variables['tid']; } } Index: modules/node/node.module =================================================================== RCS file: /cvs/drupal/drupal/modules/node/node.module,v retrieving revision 1.1202 diff -u -p -r1.1202 node.module --- modules/node/node.module 9 Jan 2010 23:03:21 -0000 1.1202 +++ modules/node/node.module 10 Jan 2010 22:14:53 -0000 @@ -1379,8 +1379,8 @@ function template_preprocess_node(&$vari } // Clean up name so there are no underscores. - $variables['template_files'][] = 'node-' . str_replace('_', '-', $node->type); - $variables['template_files'][] = 'node-' . $node->nid; + $variables['theme_hook_suggestions'][] = 'node__' . $node->type; + $variables['theme_hook_suggestions'][] = 'node__' . $node->nid; } /** Index: modules/poll/poll.module =================================================================== RCS file: /cvs/drupal/drupal/modules/poll/poll.module,v retrieving revision 1.335 diff -u -p -r1.335 poll.module --- modules/poll/poll.module 9 Jan 2010 21:54:01 -0000 1.335 +++ modules/poll/poll.module 10 Jan 2010 22:14:54 -0000 @@ -54,6 +54,16 @@ function poll_theme() { 'template' => 'poll-bar', 'variables' => array('title' => NULL, 'votes' => NULL, 'total_votes' => NULL, 'vote' => NULL, 'block' => NULL), ), + 'poll_results__block' => array( + // @todo The template file should be renamed to poll-results--block. + 'template' => 'poll-results-block', + 'variables' => array('raw_title' => NULL, 'results' => NULL, 'votes' => NULL, 'raw_links' => NULL, 'block' => NULL, 'nid' => NULL, 'vote' => NULL), + ), + 'poll_bar__block' => array( + // @todo The template file should be renamed to poll-bar--block. + 'template' => 'poll-bar-block', + 'variables' => array('raw_title' => NULL, 'results' => NULL, 'votes' => NULL, 'raw_links' => NULL, 'block' => NULL, 'nid' => NULL, 'vote' => NULL), + ), ); } @@ -733,9 +743,8 @@ function template_preprocess_poll_vote(& $variables['vote'] = drupal_render($form['vote']); $variables['rest'] = drupal_render_children($form); $variables['block'] = $form['#block']; - // If this is a block, allow a different tpl.php to be used. if ($variables['block']) { - $variables['template_files'][] = 'poll-vote-block'; + $variables['theme_hook_suggestions'][] = 'poll_vote__block'; } } @@ -833,9 +842,8 @@ function template_preprocess_poll_result } $variables['title'] = check_plain($variables['raw_title']); - // If this is a block, allow a different tpl.php to be used. if ($variables['block']) { - $variables['template_files'][] = 'poll-results-block'; + $variables['theme_hook_suggestions'][] = 'poll_results__block'; } } @@ -850,7 +858,7 @@ function template_preprocess_poll_result */ function template_preprocess_poll_bar(&$variables) { if ($variables['block']) { - $variables['template_files'][] = 'poll-bar-block'; + $variables['theme_hook_suggestions'][] = 'poll_bar__block'; } $variables['title'] = check_plain($variables['title']); $variables['percentage'] = round($variables['votes'] * 100 / max($variables['total_votes'], 1)); Index: modules/profile/profile.module =================================================================== RCS file: /cvs/drupal/drupal/modules/profile/profile.module,v retrieving revision 1.284 diff -u -p -r1.284 profile.module --- modules/profile/profile.module 14 Dec 2009 20:38:15 -0000 1.284 +++ modules/profile/profile.module 10 Jan 2010 22:14:54 -0000 @@ -585,8 +585,7 @@ function template_preprocess_profile_wra $variables['current_field'] = ''; if ($field = arg(1)) { $variables['current_field'] = $field; - // Supply an alternate template suggestion based on the browsable field. - $variables['template_files'][] = 'profile-wrapper-' . $field; + $variables['theme_hook_suggestions'][] = 'profile_wrapper__' . $field; } } Index: modules/search/search.pages.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/search/search.pages.inc,v retrieving revision 1.14 diff -u -p -r1.14 search.pages.inc --- modules/search/search.pages.inc 8 Jan 2010 17:06:09 -0000 1.14 +++ modules/search/search.pages.inc 10 Jan 2010 22:14:54 -0000 @@ -77,8 +77,7 @@ function template_preprocess_search_resu $variables['search_results'] .= theme('search_result', array('result' => $result, 'type' => $variables['type'])); } $variables['pager'] = theme('pager', array('tags' => NULL)); - // Provide alternate search results template. - $variables['template_files'][] = 'search-results-' . $variables['type']; + $variables['theme_hook_suggestions'][] = 'search_results__' . $variables['type']; } /** @@ -113,8 +112,7 @@ function template_preprocess_search_resu // Provide separated and grouped meta information.. $variables['info_split'] = $info; $variables['info'] = implode(' - ', $info); - // Provide alternate search result template. - $variables['template_files'][] = 'search-result-' . $variables['type']; + $variables['theme_hook_suggestions'][] = 'search_result__' . $variables['type']; } /** Index: modules/simpletest/tests/theme.test =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/theme.test,v retrieving revision 1.10 diff -u -p -r1.10 theme.test --- modules/simpletest/tests/theme.test 28 Dec 2009 20:58:08 -0000 1.10 +++ modules/simpletest/tests/theme.test 10 Jan 2010 22:14:54 -0000 @@ -19,36 +19,25 @@ class TemplateUnitTest extends DrupalWeb } /** - * Test function template_page_suggestions() for SA-CORE-2009-003. + * Test function theme_get_suggestions() for SA-CORE-2009-003. */ - function testTemplateSuggestions() { + function testThemeSuggestions() { // Set the front page as something random otherwise the CLI // test runner fails. variable_set('site_frontpage', 'nobody-home'); $args = array('node', '1', 'edit'); - $suggestions = template_page_suggestions($args, 'page'); - $this->assertEqual($suggestions, array('page-node', 'page-node-%', 'page-node-1', 'page-node-edit'), t('Found expected node edit page template suggestions')); + $suggestions = theme_get_suggestions($args, 'page'); + $this->assertEqual($suggestions, array('page__node', 'page__node__%', 'page__node__1', 'page__node__edit'), t('Found expected node edit page suggestions')); // Check attack vectors. $args = array('node', '\\1'); - $suggestions = template_page_suggestions($args, 'page'); - $this->assertEqual($suggestions, array('page-node', 'page-node-%', 'page-node-1'), t('Removed invalid \\ from template suggestions')); + $suggestions = theme_get_suggestions($args, 'page'); + $this->assertEqual($suggestions, array('page__node', 'page__node__%', 'page__node__1'), t('Removed invalid \\ from suggestions')); $args = array('node', '1/'); - $suggestions = template_page_suggestions($args, 'page'); - $this->assertEqual($suggestions, array('page-node', 'page-node-%', 'page-node-1'), t('Removed invalid / from template suggestions')); + $suggestions = theme_get_suggestions($args, 'page'); + $this->assertEqual($suggestions, array('page__node', 'page__node__%', 'page__node__1'), t('Removed invalid / from suggestions')); $args = array('node', "1\0"); - $suggestions = template_page_suggestions($args, 'page'); - $this->assertEqual($suggestions, array('page-node', 'page-node-%', 'page-node-1'), t('Removed invalid \\0 from template suggestions')); - // Tests for drupal_discover_template() - $suggestions = array('page'); - $this->assertEqual(drupal_discover_template(array('themes/garland'), $suggestions), 'themes/garland/page.tpl.php', t('Safe template discovered')); - $suggestions = array('page'); - $this->assertEqual(drupal_discover_template(array('themes/garland'), $suggestions, '\\.tpl.php'), 'themes/garland/page.tpl.php', t('Unsafe extension fixed')); - $suggestions = array('page\\'); - $this->assertEqual(drupal_discover_template(array('themes/garland'), $suggestions), 'themes/garland/page.tpl.php', t('Unsafe template suggestion fixed')); - $suggestions = array('page/'); - $this->assertEqual(drupal_discover_template(array('themes/garland'), $suggestions), 'themes/garland/page.tpl.php', t('Unsafe template suggestion fixed')); - $suggestions = array("page\0"); - $this->assertEqual(drupal_discover_template(array('themes/garland'), $suggestions), 'themes/garland/page.tpl.php', t('Unsafe template suggestion fixed')); + $suggestions = theme_get_suggestions($args, 'page'); + $this->assertEqual($suggestions, array('page__node', 'page__node__%', 'page__node__1'), t('Removed invalid \\0 from suggestions')); } }