diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 248c795..cb62241 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -1031,6 +1031,25 @@ function theme_disable($theme_list) {
}
/**
+ * Renders a twig string directly.
+ *
+ * @param string $template_string
+ * The template string to render with placeholders.
+ * @param array $context
+ * An array of parameters to pass to the template.
+ *
+ * @return string
+ * The rendered inline template.
+ */
+function drupal_render_twig_inline(&$element) {
+ /** @var \Drupal\Core\Template\TwigEnvironment $environment */
+ $environment = \Drupal::service('twig');
+ $markup = $environment->renderInlineTemplate($element['#template'], $element['#context']);
+ $element['#markup'] = $markup;
+ return $element;
+}
+
+/**
* @addtogroup themeable
* @{
*/
diff --git a/core/lib/Drupal/Core/Template/TwigEnvironment.php b/core/lib/Drupal/Core/Template/TwigEnvironment.php
index c671899..f0a8050 100644
--- a/core/lib/Drupal/Core/Template/TwigEnvironment.php
+++ b/core/lib/Drupal/Core/Template/TwigEnvironment.php
@@ -31,6 +31,13 @@ class TwigEnvironment extends \Twig_Environment {
protected $templateClasses;
/**
+ * The string loader implementation used for inline template rendering.
+ *
+ * @var \Twig_Loader_String
+ */
+ protected $stringLoader;
+
+ /**
* Constructs a TwigEnvironment object and stores cache and storage
* internally.
*/
@@ -38,6 +45,10 @@ public function __construct(\Twig_LoaderInterface $loader = NULL, $options = arr
// @todo Pass as arguments from the DIC.
$this->cache_object = \Drupal::cache();
+ // Ensure that twig.engine is loaded, given that it is needed to render a
+ // template because functions like twig_drupal_escape_filter are called.
+ require_once 'core/themes/engines/twig/twig.engine';
+
// Set twig path namespace for themes and modules.
$namespaces = array();
foreach ($module_handler->getModuleList() as $name => $extension) {
@@ -55,6 +66,7 @@ public function __construct(\Twig_LoaderInterface $loader = NULL, $options = arr
}
$this->templateClasses = array();
+ $this->stringLoader = new \Twig_Loader_String();
parent::__construct($loader, $options);
}
@@ -62,18 +74,21 @@ public function __construct(\Twig_LoaderInterface $loader = NULL, $options = arr
/**
* Checks if the compiled template needs an update.
*/
- public function needsUpdate($cache_filename, $name) {
- $cid = 'twig:' . $cache_filename;
- $obj = $this->cache_object->get($cid);
- $mtime = isset($obj->data) ? $obj->data : FALSE;
- return $mtime !== FALSE && !$this->isTemplateFresh($name, $mtime);
+ protected function isFresh($cache_filename, $name) {
+ $cid = 'twig:' . $cache_filename;
+ $obj = $this->cache_object->get($cid);
+ $mtime = isset($obj->data) ? $obj->data : FALSE;
+ return $mtime === FALSE || $this->isTemplateFresh($name, $mtime);
}
/**
* Compile the source and write the compiled template to disk.
+ *
+ * @param bool $inline
+ * TRUE, if the $cache_filename is a rendered template.
*/
- public function updateCompiledTemplate($cache_filename, $name) {
- $source = $this->loader->getSource($name);
+ public function updateCompiledTemplate($cache_filename, $name, $inline = FALSE) {
+ $source = $this->getLoader($inline)->getSource($name);
$compiled_source = $this->compileSource($source, $name);
$this->storage()->save($cache_filename, $compiled_source);
// Save the last modification time
@@ -82,15 +97,46 @@ public function updateCompiledTemplate($cache_filename, $name) {
}
/**
+ * Gets the Loader instance.
+ *
+ * @param bool $inline
+ * TRUE, if the string loader is requested.
+ *
+ * @return \Twig_LoaderInterface
+ * A Twig_LoaderInterface instance
+ */
+ public function getLoader($inline = FALSE) {
+ if (!isset($this->loader)) {
+ throw new \LogicException('You must set a loader first.');
+ }
+ return $inline ? $this->stringLoader : $this->loader;
+ }
+
+ /**
* Implements Twig_Environment::loadTemplate().
*
* We need to overwrite this function to integrate with drupal_php_storage().
*
* This is a straight copy from loadTemplate() changed to use
* drupal_php_storage().
+ *
+ * @param string $name
+ * The template name or the string which should be rendered as template.
+ * @param int $index
+ * The index if it is an embedded template.
+ * @param bool $inline
+ * TRUE, if the $name is a rendered template.
+ *
+ * @return \Twig_TemplateInterface
+ * A template instance representing the given template name.
+ *
+ * @throws \Twig_Error_Loader
+ * When the template cannot be found.
+ * @throws \Twig_Error_Syntax
+ * When an error occurred during compilation.
*/
- public function loadTemplate($name, $index = NULL) {
- $cls = $this->getTemplateClass($name, $index);
+ public function loadTemplate($name, $index = NULL, $inline = FALSE) {
+ $cls = $this->getTemplateClass($name, $index, $inline);
if (isset($this->loadedTemplates[$cls])) {
return $this->loadedTemplates[$cls];
@@ -100,19 +146,19 @@ public function loadTemplate($name, $index = NULL) {
$cache_filename = $this->getCacheFilename($name);
if ($cache_filename === FALSE) {
- $source = $this->loader->getSource($name);
- $compiled_source = $this->compileSource($source, $name);
+ $compiled_source = $this->compileSource($this->getLoader($inline)->getSource($name), $name);
eval('?' . '>' . $compiled_source);
- } else {
+ }
+ else {
// If autoreload is on, check that the template has not been
// modified since the last compilation.
- if ($this->isAutoReload() && $this->needsUpdate($cache_filename, $name)) {
- $this->updateCompiledTemplate($cache_filename, $name);
+ if ($this->isAutoReload() && !$this->isFresh($cache_filename, $name)) {
+ $this->updateCompiledTemplate($cache_filename, $name, $inline);
}
if (!$this->storage()->load($cache_filename)) {
- $this->updateCompiledTemplate($cache_filename, $name);
+ $this->updateCompiledTemplate($cache_filename, $name, $inline);
$this->storage()->load($cache_filename);
}
}
@@ -138,18 +184,52 @@ protected function storage() {
}
/**
- * {@inheritdoc}
+ * Gets the template class associated with the given string.
+ *
+ * @param string $name
+ * The name for which to calculate the template class name.
+ * @param int $index
+ * The index if it is an embedded template.
+ * @param bool $inline
+ * TRUE, if the $name is a rendered template.
+ *
+ * @return string
+ * The template class name.
*/
- public function getTemplateClass($name, $index = null) {
+ public function getTemplateClass($name, $index = NULL, $inline = FALSE) {
// We override this method to add caching because it gets called multiple
// times when the same template is used more than once. For example, a page
// rendering 50 nodes without any node template overrides will use the same
// node.html.twig for the output of each node and the same compiled class.
$cache_index = $name . (NULL === $index ? '' : '_' . $index);
if (!isset($this->templateClasses[$cache_index])) {
- $this->templateClasses[$cache_index] = parent::getTemplateClass($name, $index);
+ $this->templateClasses[$cache_index] = $this->templateClassPrefix . hash('sha256', $this->getLoader($inline)->getCacheKey($name)) . (NULL === $index ? '' : '_' . $index);
}
return $this->templateClasses[$cache_index];
}
+ /**
+ * Renders a twig string directly.
+ *
+ * Warning: You should use the render element 'twig_inline' together with
+ * the #template attribute instead of this method directly.
+ * On top of that you have to ensure that the template string is not dynamic
+ * but just an ordinary static php string, because there may be installations
+ * using read-only PHPStorage that want to generate all possible twig
+ * templates as part of a build step. So it is important that an automated
+ * script can find the templates and extract them. This is only possible if
+ * the template is a regular string.
+ *
+ * @param string $template_string
+ * The template string to render with placeholders.
+ * @param array $context
+ * An array of parameters to pass to the template.
+ *
+ * @return string
+ * The rendered inline template.
+ */
+ public function renderInlineTemplate($template_string, array $context = array()) {
+ return $this->loadTemplate($template_string, NULL, TRUE)->render($context);
+ }
+
}
diff --git a/core/modules/field_ui/src/DisplayOverviewBase.php b/core/modules/field_ui/src/DisplayOverviewBase.php
index f450313..0936bfa 100644
--- a/core/modules/field_ui/src/DisplayOverviewBase.php
+++ b/core/modules/field_ui/src/DisplayOverviewBase.php
@@ -404,14 +404,10 @@ protected function buildFieldRow(FieldDefinitionInterface $field_definition, Ent
$this->alterSettingsSummary($summary, $plugin, $field_definition);
if (!empty($summary)) {
- $summary_escaped = '';
- $separator = '';
- foreach ($summary as $summary_item) {
- $summary_escaped .= $separator . SafeMarkup::escape($summary_item);
- $separator = '
';
- }
$field_row['settings_summary'] = array(
- '#markup' => SafeMarkup::set('
' . String::checkPlain(strtr($query_string, $quoted)) . ''), + array('data' => array('#type' => 'twig_inline', '#template' => ' {% trans "Query" %} ')), + array('data' => array('#type' => 'twig_inline', '#template' => '
{{ query }}', '#context' => array('query' => strtr($query_string, $quoted)))), ); if (!empty($this->additionalQueries)) { $queries = '' . t('These queries were run during view rendering:') . ''; @@ -693,14 +693,14 @@ public function renderPreview($display_id, $args = array()) { } $rows['query'][] = array( - SafeMarkup::set('' . t('Other queries') . ''), + array('data' => array('#type' => 'twig_inline', '#template' => ' {% trans "Other queries" %} ')), SafeMarkup::set('
' . $queries . ''), ); } } if ($show_info) { $rows['query'][] = array( - SafeMarkup::set('' . t('Title') . ''), + array('data' => array('#type' => 'twig_inline', '#template' => ' {% trans "Title" %} ')), Xss::filterAdmin($this->executable->getTitle()), ); if (isset($path)) {