diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/TwigDebugMarkupTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/TwigDebugMarkupTest.php new file mode 100644 index 0000000000000000000000000000000000000000..401e02165651298ce1bb6890e9a2784cfc7cbccf --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Theme/TwigDebugMarkupTest.php @@ -0,0 +1,79 @@ +<?php + +/** + * @file + * Contains \Drupal\system\Tests\Theme\TwigDebugMarkupTest. + */ + +namespace Drupal\system\Tests\Theme; + +use Drupal\simpletest\WebTestBase; + +/** + * Tests for Twig debug markup. + */ +class TwigDebugMarkupTest extends WebTestBase { + + /** + * Modules to enable. + * + * @var array + */ + public static $modules = array('theme_test'); + + public static function getInfo() { + return array( + 'name' => 'Twig debug markup', + 'description' => 'Tests Twig debug markup.', + 'group' => 'Theme', + ); + } + + /** + * Tests debug markup added to Twig template output. + */ + function testTwigDebugMarkup() { + $extension = twig_extension(); + theme_enable(array('test_theme_twig')); + variable_set('theme_default', 'test_theme_twig'); + // Enable debug, rebuild the service container, and clear all caches. + $this->settingsSet('twig_debug', TRUE); + $this->rebuildContainer(); + $this->resetAll(); + + $cache = array(); + // Prime the theme cache. + foreach (module_implements('theme') as $module) { + _theme_process_registry($cache, $module, 'module', $module, drupal_get_path('module', $module)); + } + // Create array of Twig templates. + $templates = drupal_find_theme_templates($cache, $extension, drupal_get_path('theme', 'test_theme_twig')); + $templates += drupal_find_theme_templates($cache, $extension, drupal_get_path('module', 'node')); + + // Create a node and test different features of the debug markup. + $node = $this->drupalCreateNode(); + $output = theme('node', node_view($node)); + $this->assertTrue(strpos($output, '<!-- THEME DEBUG -->') !== FALSE, 'Twig debug markup found in theme output when debug is enabled.'); + $this->assertTrue(strpos($output, "CALL: theme('node')") !== FALSE, 'Theme call information found.'); + $this->assertTrue(strpos($output, 'x node--1' . $extension) !== FALSE, 'Node ID specific template shown as current template.'); + $this->assertTrue(strpos($output, '* node' . $extension) !== FALSE, 'Base template file found.'); + $template_filename = $templates['node__1']['path'] . '/' . $templates['node__1']['template'] . $extension; + $this->assertTrue(strpos($output, "BEGIN OUTPUT from '$template_filename'") !== FALSE, '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(); + $output = theme('node', node_view($node2)); + $this->assertTrue(strpos($output, '* node--2' . $extension) !== FALSE, 'Node ID specific template suggestion found.'); + $this->assertTrue(strpos($output, 'x node' . $extension) !== FALSE, 'Base template file shown as current template.'); + + // Disable debug, rebuild the service container, and clear all caches. + $this->settingsSet('twig_debug', FALSE); + $this->rebuildContainer(); + $this->resetAll(); + + $output = theme('node', node_view($node)); + $this->assertFalse(strpos($output, '<!-- THEME DEBUG -->') !== FALSE, 'Twig debug markup not found in theme output when debug is disabled.'); + } + +} diff --git a/core/themes/engines/twig/twig.engine b/core/themes/engines/twig/twig.engine index d318263fae66360959028b8d85de27e9a7a987bf..887aca149d829d041edcbd764ed4c1396f084800 100644 --- a/core/themes/engines/twig/twig.engine +++ b/core/themes/engines/twig/twig.engine @@ -31,22 +31,46 @@ function twig_init($template) { } /** - * Render twig templates. + * Renders a Twig template. * - * This retrieves the Twig_Environment from the Drupal Injection container and - * renders the template. + * If the Twig debug setting is enabled, HTML comments including theme() call + * and template file name suggestions will surround the template markup. * * @param $template_file - * The filename of the template to render. + * The file name of the template to render. * @param $variables * A keyed array of variables that will appear in the output. * * @return - * The output generated by the template. + * The output generated by the template, plus any debug information. */ function twig_render_template($template_file, $variables) { $variables['_references'] = array(); - return drupal_container()->get('twig')->loadTemplate($template_file)->render($variables); + $output = array( + 'debug_prefix' => '', + 'debug_info' => '', + 'rendered_markup' => drupal_container()->get('twig')->loadTemplate($template_file)->render($variables), + 'debug_suffix' => '', + ); + if (settings()->get('twig_debug', FALSE)) { + $output['debug_prefix'] .= "\n\n<!-- THEME DEBUG -->"; + $output['debug_prefix'] .= "\n<!-- CALL: theme('{$variables['theme_hook_original']}') -->"; + if (!empty($variables['theme_hook_suggestions'])) { + $extension = twig_extension(); + $current_template = basename($template_file); + $suggestions = $variables['theme_hook_suggestions']; + $suggestions[] = $variables['theme_hook_original']; + foreach ($suggestions as $key => &$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); } /** diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php index 4c1239ab2493023451b19536424017bf94dc161d..1356d79da9908013fd22173d23ba6ea7e5f0f535 100644 --- a/sites/default/default.settings.php +++ b/sites/default/default.settings.php @@ -286,11 +286,16 @@ /** * Twig debugging: * - * When enabled, you can use the 'dump' function in Twig templates to output - * information about variables, and templates are automatically recompiled - * whenever the source code changes. - * - * @see http://drupal.org/node/1906392 + * When debugging is enabled: + * - The markup of each Twig template is surrounded by HTML comments which + * contain theming information such as template file name suggestions. + * - The 'dump' function can be used in Twig templates to output information + * about template variables. + * - Twig templates are automatically recompiled whenever the source code + * changes (see twig_auto_reload below). + * + * For more information about debugging Twig templates, see + * http://drupal.org/node/1906392. * * Not recommended in production environments (Default: FALSE). */