diff --git a/config/vufind/searches.ini b/config/vufind/searches.ini
index 28a1223260bec36f954380de765c07b2c988a833..5efa7845082eb01db5a305ebbbb497b6a71e50ab 100644
--- a/config/vufind/searches.ini
+++ b/config/vufind/searches.ini
@@ -702,5 +702,15 @@ view=full
 ; IlsStatusMonitor:[target] - Performs an AJAX health check of the ILS and
 ; prepends a warning message to the HTML element identified by the jQuery selector
 ; provided in [target] (which defaults to .searchHomeContent.
+;
+; TemplateBased:[template] - Display the content of template defined after colon.
+; The template could be in phtml or md (markdown) format and has to be saved in
+; ContentBlock/TemplateBased/ directory of your theme. You can use language code
+; filename suffixes to present the content in different languages. For example,
+; you could add "content[] = TemplateBased:foo" to the settings below, then create
+; a ContentBlock/TemplateBased/foo.phtml (or foo.md) file in your theme's templates
+; directory to provide default content. If you wanted a different version of the
+; page for the German language, you could create a foo_de.phtml or foo_de.md file,
+; and that version would be loaded when the user's language was set to German.
 content[] = IlsStatusMonitor
 content[] = FacetList
diff --git a/module/VuFind/config/module.config.php b/module/VuFind/config/module.config.php
index af0aeb3be6898be503f99cf85710b7641f112268..5487262e2091b0ebbc842340b89693cc7c5f586a 100644
--- a/module/VuFind/config/module.config.php
+++ b/module/VuFind/config/module.config.php
@@ -352,6 +352,7 @@ $config = [
             'VuFind\Config\YamlReader' => 'VuFind\Config\YamlReaderFactory',
             'VuFind\Connection\Relais' => 'VuFind\Connection\RelaisFactory',
             'VuFind\Connection\WorldCatUtils' => 'VuFind\Connection\WorldCatUtilsFactory',
+            'VuFind\Content\PageLocator' => 'VuFind\Content\PageLocatorFactory',
             'VuFind\Content\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory',
             'VuFind\Content\AuthorNotes\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory',
             'VuFind\Content\Covers\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory',
diff --git a/module/VuFind/src/VuFind/Content/PageLocator.php b/module/VuFind/src/VuFind/Content/PageLocator.php
new file mode 100644
index 0000000000000000000000000000000000000000..9dea951fcef185139fec021a6f8fb954ee7b99fb
--- /dev/null
+++ b/module/VuFind/src/VuFind/Content/PageLocator.php
@@ -0,0 +1,123 @@
+<?php
+
+/**
+ * Class PageLocator
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Moravian Library 2020.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * @category VuFind
+ * @package  Content
+ * @author   Josef Moravec <moravec@mzk.cz>
+ * @license  https://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development Wiki
+ */
+namespace VuFind\Content;
+
+/**
+ * Class PageLocator
+ *
+ * @category VuFind
+ * @package  Content
+ * @author   Josef Moravec <moravec@mzk.cz>
+ * @license  https://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development Wiki
+ */
+class PageLocator
+{
+    /**
+     * Types/formats of content
+     *
+     * @var array $types
+     */
+    protected $types = [
+        'phtml',
+        'md',
+    ];
+
+    /**
+     * Theme info service
+     *
+     * @var \VuFindTheme\ThemeInfo
+     */
+    protected $themeInfo;
+
+    /**
+     * Current language
+     *
+     * @var string
+     */
+    protected $language;
+
+    /**
+     * Default language
+     *
+     * @var string
+     */
+    protected $defaultLanguage;
+
+    /**
+     * Page constructor.
+     *
+     * @param \VuFindTheme\ThemeInfo $themeInfo       Theme information service
+     * @param string                 $language        Current language
+     * @param string                 $defaultLanguage Main configuration
+     */
+    public function __construct($themeInfo, $language, $defaultLanguage)
+    {
+        $this->themeInfo = $themeInfo;
+        $this->language = $language;
+        $this->defaultLanguage  = $defaultLanguage;
+    }
+
+    /**
+     * Try to find template information about desired page
+     *
+     * @param string $pathPrefix Subdirectory where the template should be located
+     * @param string $pageName   Template name
+     *
+     * @return array|null Null if template is not found or array with keys renderer
+     * (type of template), path (full path of template), page (page name)
+     */
+    public function determineTemplateAndRenderer($pathPrefix, $pageName)
+    {
+        // Try to find a template using
+        // 1.) Current language suffix
+        // 2.) Default language suffix
+        // 3.) No language suffix
+        $templates = [
+            "{$pageName}_$this->language",
+            "{$pageName}_$this->defaultLanguage",
+            $pageName,
+        ];
+        foreach ($templates as $template) {
+            foreach ($this->types as $type) {
+                $filename = "$pathPrefix$template.$type";
+                $path = $this->themeInfo->findContainingTheme($filename, true);
+                if (null != $path) {
+                    return [
+                        'renderer' => $type,
+                        'path' => $path,
+                        'page' => $template,
+                    ];
+                }
+            }
+        }
+
+        return null;
+    }
+}
diff --git a/module/VuFind/src/VuFind/Content/PageLocatorFactory.php b/module/VuFind/src/VuFind/Content/PageLocatorFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..ebe8cbb3ab223da73cf0b266f4112ef58c375a50
--- /dev/null
+++ b/module/VuFind/src/VuFind/Content/PageLocatorFactory.php
@@ -0,0 +1,77 @@
+<?php
+
+/**
+ * Class PageLocatorFactory
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Moravian Library 2020.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * @category VuFind
+ * @package  Content
+ * @author   Josef Moravec <moravec@mzk.cz>
+ * @license  https://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development Wiki
+ */
+namespace VuFind\Content;
+
+use Interop\Container\ContainerInterface;
+use Interop\Container\Exception\ContainerException;
+use Laminas\ServiceManager\Exception\ServiceNotCreatedException;
+use Laminas\ServiceManager\Exception\ServiceNotFoundException;
+
+/**
+ * Page locator factory
+ *
+ * @category VuFind
+ * @package  Content
+ * @author   Josef Moravec <moravec@mzk.cz>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development Wiki
+ */
+class PageLocatorFactory
+    implements \Laminas\ServiceManager\Factory\FactoryInterface
+{
+    /**
+     * Create an object
+     *
+     * @param ContainerInterface $container     Service manager
+     * @param string             $requestedName Service being created
+     * @param null|array         $options       Extra options (optional)
+     *
+     * @return object
+     *
+     * @throws ServiceNotFoundException if unable to resolve the service.
+     * @throws ServiceNotCreatedException if an exception is raised when
+     * creating a service.
+     * @throws ContainerException if any other error occurs
+     */
+    public function __invoke(ContainerInterface $container, $requestedName,
+        array $options = null
+    ) {
+        if ($options !== null) {
+            throw new \Exception('Unexpected options sent to factory!');
+        }
+        $config = $container->get(\VuFind\Config\PluginManager::class)
+            ->get('config');
+        $defaultLanguage = $config->Site->language;
+        $themeInfo = $container->get(\VuFindTheme\ThemeInfo::class);
+        $translator = $container->get(\Laminas\Mvc\I18n\Translator::class);
+        $language = $translator->getLocale();
+
+        return new $requestedName($themeInfo, $language, $defaultLanguage);
+    }
+}
diff --git a/module/VuFind/src/VuFind/ContentBlock/PluginManager.php b/module/VuFind/src/VuFind/ContentBlock/PluginManager.php
index 1ef94d4f5cce33db109d265f48d4cf5bf436591d..c51f8321a6684c631d1838e8162132b9ac60c82f 100644
--- a/module/VuFind/src/VuFind/ContentBlock/PluginManager.php
+++ b/module/VuFind/src/VuFind/ContentBlock/PluginManager.php
@@ -49,6 +49,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager
         'channels' => Channels::class,
         'facetlist' => FacetList::class,
         'ilsstatusmonitor' => IlsStatusMonitor::class,
+        'templatebased' => TemplateBased::class,
     ];
 
     /**
@@ -60,6 +61,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager
         Channels::class => ChannelsFactory::class,
         FacetList::class => FacetListFactory::class,
         IlsStatusMonitor::class => InvokableFactory::class,
+        TemplateBased::class => TemplateBasedFactory::class,
     ];
 
     /**
diff --git a/module/VuFind/src/VuFind/ContentBlock/TemplateBased.php b/module/VuFind/src/VuFind/ContentBlock/TemplateBased.php
new file mode 100644
index 0000000000000000000000000000000000000000..52968f109628fe06462d1655da7f864b0f446824
--- /dev/null
+++ b/module/VuFind/src/VuFind/ContentBlock/TemplateBased.php
@@ -0,0 +1,128 @@
+<?php
+
+/**
+ * Class TemplateBased
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Moravian Library 2020.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * @category VuFind
+ * @package  ContentBlock
+ * @author   Josef Moravec <moravec@mzk.cz>
+ * @license  https://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development Wiki
+ */
+namespace VuFind\ContentBlock;
+
+/**
+ * Class TemplateBased
+ *
+ * @category VuFind
+ * @package  VuFind\ContentBlock
+ * @author   Josef Moravec <moravec@mzk.cz>
+ * @license  https://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development Wiki
+ */
+class TemplateBased implements ContentBlockInterface
+{
+    /**
+     * Name of template for rendering
+     *
+     * @var string
+     */
+    protected $templateName;
+
+    /**
+     * Page content
+     *
+     * @var \VuFind\Content\PageLocator
+     */
+    protected $pageLocator;
+
+    /**
+     * TemplateBased constructor.
+     *
+     * @param \VuFind\Content\PageLocator $pageLocator Content page locator service
+     */
+    public function __construct(\VuFind\Content\PageLocator $pageLocator)
+    {
+        $this->pageLocator = $pageLocator;
+    }
+
+    /**
+     * Store the configuration of the content block.
+     *
+     * @param string $settings Settings from searches.ini.
+     *
+     * @return void
+     */
+    public function setConfig($settings)
+    {
+        $this->templateName = $settings;
+    }
+
+    /**
+     * Return context variables used for rendering the block's template.
+     *
+     * @return array
+     */
+    public function getContext()
+    {
+        $page = $this->templateName;
+        $pathPrefix = "templates/ContentBlock/TemplateBased/";
+        $data = $this->pageLocator
+            ->determineTemplateAndRenderer($pathPrefix, $page);
+
+        $method = isset($data) ? 'getContextFor' . ucwords($data['renderer'])
+            : false;
+
+        return $method && is_callable([$this, $method])
+            ? $this->$method($data['page'], $data['path'])
+            : [];
+    }
+
+    /**
+     * Return context array for markdown
+     *
+     * @param string $page Page name
+     * @param string $path Full path of file
+     *
+     * @return array
+     */
+    protected function getContextForMd(string $page, string $path): array
+    {
+        return [
+            'templateName' => 'markdown',
+            'data' => file_get_contents($path),
+        ];
+    }
+
+    /**
+     * Return context array of phtml
+     *
+     * @param string $page Page name
+     * @param string $path Full path of fie
+     *
+     * @return array
+     */
+    protected function getContextForPhtml(string $page, string $path): array
+    {
+        return [
+            'templateName' => $this->templateName,
+        ];
+    }
+}
diff --git a/module/VuFind/src/VuFind/ContentBlock/TemplateBasedFactory.php b/module/VuFind/src/VuFind/ContentBlock/TemplateBasedFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..cddb9198581314a4381584e9afd42b9d4b77f9a7
--- /dev/null
+++ b/module/VuFind/src/VuFind/ContentBlock/TemplateBasedFactory.php
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * Class TemplateBasedFactory
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Moravian Library 2020.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * @category VuFind
+ * @package  VuFind\ContentBlock
+ * @author   Josef Moravec <moravec@mzk.cz>
+ * @license  https://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development Wiki
+ */
+namespace VuFind\ContentBlock;
+
+use Interop\Container\ContainerInterface;
+use Interop\Container\Exception\ContainerException;
+use Laminas\ServiceManager\Exception\ServiceNotCreatedException;
+use Laminas\ServiceManager\Exception\ServiceNotFoundException;
+
+/**
+ * TemplateBased factory
+ *
+ * @category VuFind
+ * @package  ContentBlock
+ * @author   Josef Moravec <moravec@mzk.cz>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development Wiki
+ */
+class TemplateBasedFactory
+    implements \Laminas\ServiceManager\Factory\FactoryInterface
+{
+    /**
+     * Create an object
+     *
+     * @param ContainerInterface $container     Service manager
+     * @param string             $requestedName Service being created
+     * @param null|array         $options       Extra options (optional)
+     *
+     * @return object
+     *
+     * @throws ServiceNotFoundException if unable to resolve the service.
+     * @throws ServiceNotCreatedException if an exception is raised when
+     * creating a service.
+     * @throws ContainerException if any other error occurs
+     */
+    public function __invoke(ContainerInterface $container, $requestedName,
+        array $options = null
+    ) {
+        if ($options !== null) {
+            throw new \Exception('Unexpected options sent to factory!');
+        }
+        $pageContent = $container->get(\VuFind\Content\PageLocator::class);
+
+        return new $requestedName($pageContent);
+    }
+}
diff --git a/module/VuFind/src/VuFind/Controller/ContentController.php b/module/VuFind/src/VuFind/Controller/ContentController.php
index 1efbd564104b62a925e1885bf52d1419704f91c6..4ce7359a74bc61585caa27627b06e36df7bacac6 100644
--- a/module/VuFind/src/VuFind/Controller/ContentController.php
+++ b/module/VuFind/src/VuFind/Controller/ContentController.php
@@ -62,39 +62,15 @@ class ContentController extends AbstractBase
     public function contentAction()
     {
         $page = $this->params()->fromRoute('page');
-        $themeInfo = $this->serviceLocator->get(\VuFindTheme\ThemeInfo::class);
-        $language = $this->serviceLocator->get(\Laminas\Mvc\I18n\Translator::class)
-            ->getLocale();
-        $defaultLanguage = $this->getConfig()->Site->language;
-
-        // Try to find a template using
-        // 1.) Current language suffix
-        // 2.) Default language suffix
-        // 3.) No language suffix
-        $templates = [
-            "{$page}_$language",
-            "{$page}_$defaultLanguage",
-            $page,
-        ];
-
         $pathPrefix = "templates/content/";
+        $pageLocator = $this->serviceLocator
+            ->get(\VuFind\Content\PageLocator::class);
+        $data = $pageLocator->determineTemplateAndRenderer($pathPrefix, $page);
 
-        foreach ($templates as $template) {
-            foreach ($this->types as $type) {
-                $filename = "$pathPrefix$template.$type";
-                $path = $themeInfo->findContainingTheme($filename, true);
-                if (null != $path) {
-                    $page = $template;
-                    $renderer = $type;
-                    break 2;
-                }
-            }
-        }
-
-        $method = isset($renderer) ? 'getViewFor' . ucwords($renderer) : false;
+        $method = isset($data) ? 'getViewFor' . ucwords($data['renderer']) : false;
 
         return $method && is_callable([$this, $method])
-            ? $this->$method($page, $path)
+            ? $this->$method($data['page'], $data['path'])
             : $this->notFoundAction($this->getResponse());
     }
 
diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/PageLocatorTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/PageLocatorTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..578830b1282389ff8563e2e28b7b36867a978f5a
--- /dev/null
+++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/PageLocatorTest.php
@@ -0,0 +1,135 @@
+<?php
+
+/**
+ * Class PageLocatorTest
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Moravian Library 2020.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * @category VuFind
+ * @package  Tests
+ * @author   Josef Moravec <moravec@mzk.cz>
+ * @license  https://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development:testing:unit_tests Wiki
+ */
+namespace VuFindTest\Content;
+
+use VuFind\Content\PageLocator;
+use VuFindTheme\ThemeInfo;
+
+/**
+ * Content Page Locator Test Class
+ *
+ * @category VuFind
+ * @package  Tests
+ * @author   Josef Moravec <moravec@mzk.cz>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development:testing:unit_tests Wiki
+ */
+class PageLocatorTest extends \VuFindTest\Unit\TestCase
+{
+    /**
+     * Path to theme fixtures
+     *
+     * @var string
+     */
+    protected $fixturePath;
+
+    /**
+     * Constructor
+     */
+    public function setUp(): void
+    {
+        $this->fixturePath = realpath(__DIR__ . '/../../../../../../VuFindTheme/tests/unit-tests/fixtures/themes');
+    }
+
+    public function testDetermineTemplateAndRenderer()
+    {
+        $language  = 'aa';
+        $defaultLanguage = 'bb';
+        $pathPrefix = 'templates/page-locator-test/';
+        $testCases = [
+            [
+                'pageName' => 'page1',
+                'result' => [
+                    'renderer' => 'phtml',
+                    'path' => $this->fixturePath . '/parent/templates/page-locator-test/page1.phtml',
+                    'page' => 'page1',
+                ],
+            ],
+            [
+                'pageName' => 'page2',
+                'result' => [
+                    'renderer' => 'phtml',
+                    'path' => $this->fixturePath . '/parent/templates/page-locator-test/page2_aa.phtml',
+                    'page' => 'page2_aa',
+                ],
+            ],
+            [
+                'pageName' => 'page3',
+                'result' => [
+                    'renderer' => 'phtml',
+                    'path' => $this->fixturePath . '/parent/templates/page-locator-test/page3_bb.phtml',
+                    'page' => 'page3_bb',
+                ],
+            ],
+            [
+                'pageName' => 'page4',
+                'result' => [
+                    'renderer' => 'md',
+                    'path' => $this->fixturePath . '/parent/templates/page-locator-test/page4.md',
+                    'page' => 'page4',
+                ],
+            ],
+            [
+                'pageName' => 'page5',
+                'result' => [
+                    'renderer' => 'md',
+                    'path' => $this->fixturePath . '/parent/templates/page-locator-test/page5_aa.md',
+                    'page' => 'page5_aa',
+                ],
+            ],
+            [
+                'pageName' => 'page6',
+                'result' => [
+                    'renderer' => 'md',
+                    'path' => $this->fixturePath . '/parent/templates/page-locator-test/page6_bb.md',
+                    'page' => 'page6_bb',
+                ],
+            ],
+            [
+                'pageName' => 'non-existant-page',
+                'result' => null,
+            ],
+        ];
+        $themeInfo = $this->getThemeInfo();
+        $pageLocator = new PageLocator($themeInfo, $language, $defaultLanguage);
+        foreach ($testCases as $case) {
+            $this->assertEquals($case['result'], $pageLocator->determineTemplateAndRenderer($pathPrefix, $case['pageName']));
+        }
+    }
+
+    /**
+     * Get a test object
+     *
+     * @return ThemeInfo
+     */
+    protected function getThemeInfo()
+    {
+        return new ThemeInfo($this->fixturePath, 'parent');
+    }
+}
diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ContentBlock/TemplateBasedTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ContentBlock/TemplateBasedTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..b9f1b10db812f9605b38de5e940481c9a45f3fb3
--- /dev/null
+++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ContentBlock/TemplateBasedTest.php
@@ -0,0 +1,78 @@
+<?php
+/**
+ * TemplateBased ContentBlack Test Class
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 2020.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * @category VuFind
+ * @package  Tests
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development:testing:unit_tests Wiki
+ */
+namespace VuFindTest\ContentBlock;
+
+/**
+ * TemplateBased ContentBlack Test Class
+ *
+ * @category VuFind
+ * @package  Tests
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development:testing:unit_tests Wiki
+ */
+class TemplateBasedTest extends \PHPUnit\Framework\TestCase
+{
+    /**
+     * Test basic functionality of .phtml content block.
+     *
+     * @return void
+     */
+    public function testBasicPhtmlFunctionality()
+    {
+        $locator = $this->getMockBuilder(\VuFind\Content\PageLocator::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $locator->expects($this->once())->method('determineTemplateAndRenderer')
+            ->with($this->equalTo('templates/ContentBlock/TemplateBased/'), $this->equalTo('foo'))
+            ->will($this->returnValue(['renderer' => 'phtml', 'page' => 'foo', 'path' => '/path/to/foo.phtml']));
+        $block = new \VuFind\ContentBlock\TemplateBased($locator);
+        $block->setConfig('foo');
+        $this->assertEquals(['templateName' => 'foo'], $block->getContext());
+    }
+
+    /**
+     * Test basic functionality of Markdown content block.
+     *
+     * @return void
+     */
+    public function testBasicMarkdownFunctionality()
+    {
+        $fixturePath = realpath(__DIR__ . '/../../../../../../VuFindTheme/tests/unit-tests/fixtures/themes');
+        $file = $fixturePath . '/parent/templates/page-locator-test/page4.md';
+        $locator = $this->getMockBuilder(\VuFind\Content\PageLocator::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $locator->expects($this->once())->method('determineTemplateAndRenderer')
+            ->with($this->equalTo('templates/ContentBlock/TemplateBased/'), $this->equalTo($file))
+            ->will($this->returnValue(['renderer' => 'md', 'page' => 'page4', 'path' => $file]));
+        $block = new \VuFind\ContentBlock\TemplateBased($locator);
+        $block->setConfig($file);
+        $this->assertEquals(['templateName' => 'markdown', 'data' => file_get_contents($file)], $block->getContext());
+    }
+}
diff --git a/module/VuFindTheme/tests/unit-tests/fixtures/themes/parent/templates/page-locator-test/page1.md b/module/VuFindTheme/tests/unit-tests/fixtures/themes/parent/templates/page-locator-test/page1.md
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/module/VuFindTheme/tests/unit-tests/fixtures/themes/parent/templates/page-locator-test/page1.phtml b/module/VuFindTheme/tests/unit-tests/fixtures/themes/parent/templates/page-locator-test/page1.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/module/VuFindTheme/tests/unit-tests/fixtures/themes/parent/templates/page-locator-test/page2_aa.phtml b/module/VuFindTheme/tests/unit-tests/fixtures/themes/parent/templates/page-locator-test/page2_aa.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/module/VuFindTheme/tests/unit-tests/fixtures/themes/parent/templates/page-locator-test/page2_bb.phtml b/module/VuFindTheme/tests/unit-tests/fixtures/themes/parent/templates/page-locator-test/page2_bb.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/module/VuFindTheme/tests/unit-tests/fixtures/themes/parent/templates/page-locator-test/page3.phtml b/module/VuFindTheme/tests/unit-tests/fixtures/themes/parent/templates/page-locator-test/page3.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/module/VuFindTheme/tests/unit-tests/fixtures/themes/parent/templates/page-locator-test/page3_bb.phtml b/module/VuFindTheme/tests/unit-tests/fixtures/themes/parent/templates/page-locator-test/page3_bb.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/module/VuFindTheme/tests/unit-tests/fixtures/themes/parent/templates/page-locator-test/page4.md b/module/VuFindTheme/tests/unit-tests/fixtures/themes/parent/templates/page-locator-test/page4.md
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/module/VuFindTheme/tests/unit-tests/fixtures/themes/parent/templates/page-locator-test/page5_aa.md b/module/VuFindTheme/tests/unit-tests/fixtures/themes/parent/templates/page-locator-test/page5_aa.md
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/module/VuFindTheme/tests/unit-tests/fixtures/themes/parent/templates/page-locator-test/page5_bb.md b/module/VuFindTheme/tests/unit-tests/fixtures/themes/parent/templates/page-locator-test/page5_bb.md
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/module/VuFindTheme/tests/unit-tests/fixtures/themes/parent/templates/page-locator-test/page6.md b/module/VuFindTheme/tests/unit-tests/fixtures/themes/parent/templates/page-locator-test/page6.md
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/module/VuFindTheme/tests/unit-tests/fixtures/themes/parent/templates/page-locator-test/page6_bb.md b/module/VuFindTheme/tests/unit-tests/fixtures/themes/parent/templates/page-locator-test/page6_bb.md
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/themes/bootstrap3/templates/ContentBlock/TemplateBased.phtml b/themes/bootstrap3/templates/ContentBlock/TemplateBased.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..c2b459a148cdfc6ca58e6ab38f0d520557247940
--- /dev/null
+++ b/themes/bootstrap3/templates/ContentBlock/TemplateBased.phtml
@@ -0,0 +1 @@
+<?=$this->render('ContentBlock/TemplateBased/' . $this->templateName); ?>
diff --git a/themes/bootstrap3/templates/ContentBlock/TemplateBased/markdown.phtml b/themes/bootstrap3/templates/ContentBlock/TemplateBased/markdown.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..703e4ebb4a0a25b0efa93ad8736d9241938f0552
--- /dev/null
+++ b/themes/bootstrap3/templates/ContentBlock/TemplateBased/markdown.phtml
@@ -0,0 +1 @@
+<?=$this->markdown($this->data);