diff --git a/core/lib/Drupal/Core/Theme/Registry.php b/core/lib/Drupal/Core/Theme/Registry.php index 0ad005d..1c883d5 100644 --- a/core/lib/Drupal/Core/Theme/Registry.php +++ b/core/lib/Drupal/Core/Theme/Registry.php @@ -401,6 +401,9 @@ protected function build() { * @see _theme() * @see hook_theme() * @see list_themes() + * @see twig_render_template() + * + * @throws \BadFunctionCallException */ protected function processExtension(&$cache, $name, $type, $theme, $path) { $result = array(); @@ -430,12 +433,6 @@ protected function processExtension(&$cache, $name, $type, $theme, $path) { $result[$hook]['type'] = $type; $result[$hook]['theme path'] = $path; - // If function and file are omitted, default to standard naming - // conventions. - if (!isset($info['template']) && !isset($info['function'])) { - $result[$hook]['function'] = ($type == 'module' ? 'theme_' : $name . '_') . $hook; - } - if (isset($cache[$hook]['includes'])) { $result[$hook]['includes'] = $cache[$hook]['includes']; } @@ -451,20 +448,33 @@ protected function processExtension(&$cache, $name, $type, $theme, $path) { $result[$hook]['includes'][] = $include_file; } + // Templates are the default implementation for a theme hook. If a + // theme hook provides a theme function callback, check to ensure that + // it actually exists. + if (isset($info['function']) && !function_exists($info['function'])) { + continue; + } + // Provide a default naming convention for 'template' based on the + // hook used. If the template does not exist, the theme engine used + // should throw an exception at runtime when attempting to include + // the template file. + elseif (!isset($info['template'])) { + $info['template'] = strtr($hook, '_', '-'); + $result[$hook]['template'] = $info['template']; + } + + // Prepend the current theming path when none is set. This is required + // for the default theme engine to know where the template lives. + if (isset($result[$hook]['template']) && !isset($info['path'])) { + $result[$hook]['path'] = $path . '/templates'; + } + // If the default keys are not set, use the default values registered // by the module. if (isset($cache[$hook])) { $result[$hook] += array_intersect_key($cache[$hook], $hook_defaults); } - // The following apply only to theming hooks implemented as templates. - if (isset($info['template'])) { - // Prepend the current theming path when none is set. - if (!isset($info['path'])) { - $result[$hook]['template'] = $path . '/templates/' . $info['template']; - } - } - // Preprocess variables for all theming hooks, whether the hook is // implemented as a template or as a function. Ensure they are arrays. if (!isset($info['preprocess functions']) || !is_array($info['preprocess functions'])) { diff --git a/core/modules/system/theme.api.php b/core/modules/system/theme.api.php index 089334f..18a2cb7 100644 --- a/core/modules/system/theme.api.php +++ b/core/modules/system/theme.api.php @@ -49,7 +49,6 @@ * 'plugin_id' => NULL, * ), * 'file' => 'search.pages.inc', - * 'template' => 'search-result', * ), * ); * @endcode diff --git a/core/modules/views/views.module b/core/modules/views/views.module index 3ba93f5..8ac542c 100644 --- a/core/modules/views/views.module +++ b/core/modules/views/views.module @@ -187,35 +187,44 @@ function views_theme($existing, $type, $theme, $path) { 'variables' => $variables[$type], ); - // For the views module we ensure views.theme.inc is included. - if ($def['provider'] == 'views') { - $def['theme_file'] = 'views.theme.inc'; - } // We always use the module directory as base dir. $module_dir = drupal_get_path('module', $def['provider']); + $hooks[$def['theme']]['path'] = $module_dir; + // For the views module we ensure views.theme.inc is included. + if ($def['provider'] == 'views') { + if (!isset($hooks[$def['theme']]['includes'])) { + $hooks[$def['theme']]['includes'] = array(); + } + if (!in_array('views.theme.inc', $hooks[$def['theme']]['includes'])) { + $hooks[$def['theme']]['includes'][] = $module_dir . '/views.theme.inc'; + } + } // The theme_file definition is always relative to the modules directory. - if (isset($def['theme_file'])) { - $hooks[$def['theme']]['path'] = $module_dir; + elseif (!empty($def['theme_file'])) { $hooks[$def['theme']]['file'] = $def['theme_file']; } + // Whenever we got a theme file, we include it directly so we can // auto-detect the theme function. if (isset($def['theme_file'])) { - $include = DRUPAL_ROOT . '/' . $module_dir. '/' . $def['theme_file']; + $include = DRUPAL_ROOT . '/' . $module_dir . '/' . $def['theme_file']; if (is_file($include)) { require_once $include; } } - // If there is no theme function for the given theme definition, we assume - // a template file shall be used. By default this file is located in the - // /templates directory of the module's folder. - // If a module wants to define its own location it has to set - // register_theme of the plugin to FALSE and implement hook_theme() by - // itself. + + // If there is no theme function for the given theme definition, it must + // be a template file. By default this file is located in the /templates + // directory of the module's folder. If a module wants to define its own + // location it has to set register_theme of the plugin to FALSE and + // implement hook_theme() by itself. if (!function_exists('theme_' . $def['theme'])) { - $hooks[$def['theme']]['path'] = $module_dir; - $hooks[$def['theme']]['template'] = 'templates/' . drupal_clean_css_identifier($def['theme']); + $hooks[$def['theme']]['path'] .= '/templates'; + $hooks[$def['theme']]['template'] = drupal_clean_css_identifier($def['theme']); + } + else { + $hooks[$def['theme']]['function'] = 'theme_' . $def['theme']; } } } diff --git a/core/themes/engines/twig/twig.engine b/core/themes/engines/twig/twig.engine index e0cfcc2..06ec07c 100644 --- a/core/themes/engines/twig/twig.engine +++ b/core/themes/engines/twig/twig.engine @@ -38,23 +38,28 @@ function twig_init(Extension $theme) { * 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 + * @param string $template_file * The file name of the template to render. - * @param $variables + * @param array $variables * A keyed array of variables that will appear in the output. * - * @return + * @return string * The output generated by the template, plus any debug information. */ function twig_render_template($template_file, $variables) { /** @var \Twig_Environment $twig_service */ $twig_service = \Drupal::service('twig'); - $output = array( - 'debug_prefix' => '', - 'debug_info' => '', - 'rendered_markup' => $twig_service->loadTemplate($template_file)->render($variables), - 'debug_suffix' => '', - ); + try { + $output = array( + 'debug_prefix' => '', + 'debug_info' => '', + 'rendered_markup' => $twig_service->loadTemplate($template_file)->render($variables), + 'debug_suffix' => '', + ); + } + catch (\Twig_Error_Loader $e) { + drupal_set_message($e->getMessage(), 'error'); + } if ($twig_service->isDebug()) { $output['debug_prefix'] .= "\n\n"; $output['debug_prefix'] .= "\n";