summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.txt3
-rw-r--r--includes/theme.inc76
-rw-r--r--modules/simpletest/tests/theme.test65
-rw-r--r--modules/simpletest/tests/themes/test_theme/templates/node--1.tpl.php2
4 files changed, 145 insertions, 1 deletions
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 47d51ced1..352a235b0 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,6 +1,9 @@
Drupal 7.33, xxxx-xx-xx (development version)
-----------------------
+- Added a "theme_hook_original" variable to templates and theme functions and
+ an optional sitewide theme debug mode, to provide contextual information in
+ the page's HTML to theme developers (API addition).
- Added an entity_view_mode_prepare() API function to allow entity-defining
modules to properly invoke hook_entity_view_mode_alter(), and used it
throughout Drupal core to fix bugs with the invocation of that hook.
diff --git a/includes/theme.inc b/includes/theme.inc
index ebe5817b2..d2a142b64 100644
--- a/includes/theme.inc
+++ b/includes/theme.inc
@@ -1029,6 +1029,7 @@ function theme($hook, $variables = array()) {
}
$hook = $candidate;
}
+ $theme_hook_original = $hook;
// If there's no implementation, check for more generic fallbacks. If there's
// still no implementation, log an error and return an empty string.
@@ -1090,6 +1091,8 @@ function theme($hook, $variables = array()) {
$variables += array($info['render element'] => array());
}
+ $variables['theme_hook_original'] = $theme_hook_original;
+
// Invoke the variable processors, if any. The processors may specify
// alternate suggestions for which hook's template/function to use. If the
// hook is a suggestion of a base hook, invoke the variable processors of
@@ -1198,7 +1201,12 @@ function theme($hook, $variables = array()) {
if (isset($info['path'])) {
$template_file = $info['path'] . '/' . $template_file;
}
- $output = $render_function($template_file, $variables);
+ if (variable_get('theme_debug', FALSE)) {
+ $output = _theme_render_template_debug($render_function, $template_file, $variables, $extension);
+ }
+ else {
+ $output = $render_function($template_file, $variables);
+ }
}
// restore path_to_theme()
@@ -1521,6 +1529,72 @@ function theme_render_template($template_file, $variables) {
}
/**
+ * Renders a template for any engine.
+ *
+ * Includes the possibility to get debug output by setting the
+ * theme_debug variable to TRUE.
+ *
+ * @param string $template_function
+ * The function to call for rendering the template.
+ * @param string $template_file
+ * The filename of the template to render.
+ * @param array $variables
+ * A keyed array of variables that will appear in the output.
+ * @param string $extension
+ * The extension used by the theme engine for template files.
+ *
+ * @return string
+ * The output generated by the template including debug information.
+ */
+function _theme_render_template_debug($template_function, $template_file, $variables, $extension) {
+ $output = array(
+ 'debug_prefix' => '',
+ 'debug_info' => '',
+ 'rendered_markup' => call_user_func($template_function, $template_file, $variables),
+ 'debug_suffix' => '',
+ );
+ $output['debug_prefix'] .= "\n\n<!-- THEME DEBUG -->";
+ $output['debug_prefix'] .= "\n<!-- CALL: theme('{$variables['theme_hook_original']}') -->";
+ // If there are theme suggestions, reverse the array so more specific
+ // suggestions are shown first.
+ if (!empty($variables['theme_hook_suggestions'])) {
+ $variables['theme_hook_suggestions'] = array_reverse($variables['theme_hook_suggestions']);
+ }
+ // Add debug output for directly called suggestions like
+ // '#theme' => 'comment__node__article'.
+ if (strpos($variables['theme_hook_original'], '__') !== FALSE) {
+ $derived_suggestions[] = $hook = $variables['theme_hook_original'];
+ while ($pos = strrpos($hook, '__')) {
+ $hook = substr($hook, 0, $pos);
+ $derived_suggestions[] = $hook;
+ }
+ // Get the value of the base hook (last derived suggestion) and append it
+ // to the end of all theme suggestions.
+ $base_hook = array_pop($derived_suggestions);
+ $variables['theme_hook_suggestions'] = array_merge($derived_suggestions, $variables['theme_hook_suggestions']);
+ $variables['theme_hook_suggestions'][] = $base_hook;
+ }
+ if (!empty($variables['theme_hook_suggestions'])) {
+ $current_template = basename($template_file);
+ $suggestions = $variables['theme_hook_suggestions'];
+ // Only add the original theme hook if it wasn't a directly called
+ // suggestion.
+ if (strpos($variables['theme_hook_original'], '__') === FALSE) {
+ $suggestions[] = $variables['theme_hook_original'];
+ }
+ foreach ($suggestions as &$suggestion) {
+ $template = strtr($suggestion, '_', '-') . $extension;
+ $prefix = ($template == $current_template) ? 'x' : '*';
+ $suggestion = $prefix . ' ' . $template;
+ }
+ $output['debug_info'] .= "\n<!-- FILE NAME SUGGESTIONS:\n " . implode("\n ", $suggestions) . "\n-->";
+ }
+ $output['debug_info'] .= "\n<!-- BEGIN OUTPUT from '{$template_file}' -->\n";
+ $output['debug_suffix'] .= "\n<!-- END OUTPUT from '{$template_file}' -->\n\n";
+ return implode('', $output);
+}
+
+/**
* Enables a given list of themes.
*
* @param $theme_list
diff --git a/modules/simpletest/tests/theme.test b/modules/simpletest/tests/theme.test
index 519a7a90a..328afec44 100644
--- a/modules/simpletest/tests/theme.test
+++ b/modules/simpletest/tests/theme.test
@@ -500,3 +500,68 @@ class ThemeRegistryTestCase extends DrupalWebTestCase {
$this->assertTrue($registry['theme_test_template_test_2'], 'Offset was returned correctly from the theme registry');
}
}
+
+/**
+ * Tests for theme debug markup.
+ */
+class ThemeDebugMarkupTestCase extends DrupalWebTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Theme debug markup',
+ 'description' => 'Tests theme debug markup output.',
+ 'group' => 'Theme',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('theme_test', 'node');
+ theme_enable(array('test_theme'));
+ }
+
+ /**
+ * Tests debug markup added to template output.
+ */
+ function testDebugOutput() {
+ variable_set('theme_default', 'test_theme');
+ // Enable the debug output.
+ variable_set('theme_debug', TRUE);
+
+ $registry = theme_get_registry();
+ $extension = '.tpl.php';
+ // Populate array of templates.
+ $templates = drupal_find_theme_templates($registry, $extension, drupal_get_path('theme', 'test_theme'));
+ $templates += drupal_find_theme_templates($registry, $extension, drupal_get_path('module', 'node'));
+
+ // Create a node and test different features of the debug markup.
+ $node = $this->drupalCreateNode();
+ $this->drupalGet('node/' . $node->nid);
+ $this->assertRaw('<!-- THEME DEBUG -->', 'Theme debug markup found in theme output when debug is enabled.');
+ $this->assertRaw("CALL: theme('node')", 'Theme call information found.');
+ $this->assertRaw('x node--1' . $extension . PHP_EOL . ' * node--page' . $extension . PHP_EOL . ' * node' . $extension, 'Suggested template files found in order and node ID specific template shown as current template.');
+ $template_filename = $templates['node__1']['path'] . '/' . $templates['node__1']['template'] . $extension;
+ $this->assertRaw("BEGIN OUTPUT from '$template_filename'", 'Full path to current template file found.');
+
+ // Create another node and make sure the template suggestions shown in the
+ // debug markup are correct.
+ $node2 = $this->drupalCreateNode();
+ $this->drupalGet('node/' . $node2->nid);
+ $this->assertRaw('* node--2' . $extension . PHP_EOL . ' * node--page' . $extension . PHP_EOL . ' x node' . $extension, 'Suggested template files found in order and base template shown as current template.');
+
+ // Create another node and make sure the template suggestions shown in the
+ // debug markup are correct.
+ $node3 = $this->drupalCreateNode();
+ $build = array('#theme' => 'node__foo__bar');
+ $build += node_view($node3);
+ $output = drupal_render($build);
+ $this->assertTrue(strpos($output, "CALL: theme('node__foo__bar')") !== FALSE, 'Theme call information found.');
+ $this->assertTrue(strpos($output, '* node--foo--bar' . $extension . PHP_EOL . ' * node--foo' . $extension . PHP_EOL . ' * node--3' . $extension . PHP_EOL . ' * node--page' . $extension . PHP_EOL . ' x node' . $extension) !== FALSE, 'Suggested template files found in order and base template shown as current template.');
+
+ // Disable theme debug.
+ variable_set('theme_debug', FALSE);
+
+ $this->drupalGet('node/' . $node->nid);
+ $this->assertNoRaw('<!-- THEME DEBUG -->', 'Theme debug markup not found in theme output when debug is disabled.');
+ }
+
+}
diff --git a/modules/simpletest/tests/themes/test_theme/templates/node--1.tpl.php b/modules/simpletest/tests/themes/test_theme/templates/node--1.tpl.php
new file mode 100644
index 000000000..87aa79477
--- /dev/null
+++ b/modules/simpletest/tests/themes/test_theme/templates/node--1.tpl.php
@@ -0,0 +1,2 @@
+<!-- Output for Theme Debug Markup test -->
+Node Content Dummy