From 61d2686b74f87a460b584239da18fd95d4698431 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jaime=20Pe=CC=81rez=20Crespo?= <jaime.perez@uninett.no>
Date: Wed, 6 Dec 2017 14:22:53 +0100
Subject: [PATCH] Use a custom Twig template loader.

This allows us to load templates from modules inside a twig template itself (include, embed, etc), even when the main template is not in a module or in a different one.
---
 lib/SimpleSAML/XHTML/Template.php       | 33 ++++---------------
 lib/SimpleSAML/XHTML/TemplateLoader.php | 44 +++++++++++++++++++++++++
 2 files changed, 51 insertions(+), 26 deletions(-)
 create mode 100644 lib/SimpleSAML/XHTML/TemplateLoader.php

diff --git a/lib/SimpleSAML/XHTML/Template.php b/lib/SimpleSAML/XHTML/Template.php
index fdcee57df..49ca3c861 100644
--- a/lib/SimpleSAML/XHTML/Template.php
+++ b/lib/SimpleSAML/XHTML/Template.php
@@ -11,6 +11,7 @@
 
 use JaimePerez\TwigConfigurableI18n\Twig\Environment as Twig_Environment;
 use JaimePerez\TwigConfigurableI18n\Twig\Extensions\Extension\I18n as Twig_Extensions_Extension_I18n;
+use \SimpleSAML\XHTML\TemplateLoader;
 
 
 class SimpleSAML_XHTML_Template
@@ -167,14 +168,16 @@ class SimpleSAML_XHTML_Template
         // get namespace if any
         list($namespace, $filename) = self::findModuleAndTemplateName($filename);
         $this->twig_template = ($namespace !== null) ? '@'.$namespace.'/'.$filename : $filename;
-        $loader = new \Twig_Loader_Filesystem();
+        $loader = new TemplateLoader();
         $templateDirs = $this->findThemeTemplateDirs();
         if ($this->module) {
-            $templateDirs[] = array($this->module => $this->getModuleTemplateDir($this->module));
+            $templateDirs[] = array($this->module => TemplateLoader::getModuleTemplateDir($this->module));
         }
         if ($this->theme['module']) {
             try {
-                $templateDirs[] = array($this->theme['module'] => $this->getModuleTemplateDir($this->theme['module']));
+                $templateDirs[] = array(
+                    $this->theme['module'] =>TemplateLoader::getModuleTemplateDir($this->theme['module'])
+                );
             } catch (\InvalidArgumentException $e) {
                 // either the module is not enabled or it has no "templates" directory, ignore
             }
@@ -299,28 +302,6 @@ class SimpleSAML_XHTML_Template
         return $themeTemplateDirs;
     }
 
-    /**
-     * Get the template directory of a module, if it exists.
-     *
-     * @return string The templates directory of a module.
-     *
-     * @throws InvalidArgumentException If the module is not enabled or it has no templates directory.
-     */
-    private function getModuleTemplateDir($module)
-    {
-        if (!\SimpleSAML\Module::isModuleEnabled($module)) {
-            throw new InvalidArgumentException('The module \''.$module.'\' is not enabled.');
-        }
-        $moduledir = \SimpleSAML\Module::getModuleDir($module);
-        // check if module has a /templates dir, if so, append
-        $templatedir = $moduledir.'/templates';
-        if (!is_dir($templatedir)) {
-            throw new InvalidArgumentException('The module \''.$module.'\' has no templates directory.');
-
-        }
-        return $templatedir;
-    }
-
 
     /**
      * Add the templates from a given module.
@@ -333,7 +314,7 @@ class SimpleSAML_XHTML_Template
      */
     public function addTemplatesFromModule($module)
     {
-        $dir = $this->getModuleTemplateDir($module);
+        $dir = TemplateLoader::getModuleTemplateDir($module);
         /** @var Twig_Loader_Filesystem $loader */
         $loader = $this->twig->getLoader();
         $loader->addPath($dir, $module);
diff --git a/lib/SimpleSAML/XHTML/TemplateLoader.php b/lib/SimpleSAML/XHTML/TemplateLoader.php
new file mode 100644
index 000000000..3b95d6e36
--- /dev/null
+++ b/lib/SimpleSAML/XHTML/TemplateLoader.php
@@ -0,0 +1,44 @@
+<?php
+
+
+namespace SimpleSAML\XHTML;
+
+
+class TemplateLoader extends \Twig\Loader\FilesystemLoader
+{
+    /**
+     * This method adds a namespace dynamically so that we can load templates from modules whenever we want.
+     *
+     * @inheritdoc
+     */
+    protected function findTemplate($name)
+    {
+        list($namespace, $shortname) = $this->parseName($name);
+        if (!in_array($namespace, $this->paths, true) && $namespace !== self::MAIN_NAMESPACE) {
+            $this->addPath(self::getModuleTemplateDir($namespace), $namespace);
+        }
+        return parent::findTemplate($name);
+    }
+
+    /**
+     * Get the template directory of a module, if it exists.
+     *
+     * @return string The templates directory of a module.
+     *
+     * @throws \InvalidArgumentException If the module is not enabled or it has no templates directory.
+     */
+    public static function getModuleTemplateDir($module)
+    {
+        if (!\SimpleSAML\Module::isModuleEnabled($module)) {
+            throw new \InvalidArgumentException('The module \''.$module.'\' is not enabled.');
+        }
+        $moduledir = \SimpleSAML\Module::getModuleDir($module);
+        // check if module has a /templates dir, if so, append
+        $templatedir = $moduledir.'/templates';
+        if (!is_dir($templatedir)) {
+            throw new \InvalidArgumentException('The module \''.$module.'\' has no templates directory.');
+
+        }
+        return $templatedir;
+    }
+}
\ No newline at end of file
-- 
GitLab