Skip to content

Commit

Permalink
Better support for new template names (#13)
Browse files Browse the repository at this point in the history
* working on using contao template loader

* support legacy naming

* remove test code

* cleanup

* adjust tests

* fixed missing folder scan, fixed tests

* ignore coverage of deprecated method

* working on tests and theme support

* correctly load theme templates

* fixed gitignore paths

* test template cache
  • Loading branch information
koertho authored Sep 26, 2024
1 parent ca5fa02 commit 5425f89
Show file tree
Hide file tree
Showing 23 changed files with 217 additions and 84 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ jobs:
strategy:
fail-fast: false
matrix:
php: [ 7.4, 8.0, 8.1 ]
contao: [ 4.9.*, 4.13.* ]
php: [ 7.4, 8.0, 8.1, 8.2, 8.3 ]
contao: [ 4.13.* ]

steps:
- name: Setup PHP
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
composer.lock
.idea
vendor
/vendor
.phpunit.result.cache
.ddev
build
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"license": "LGPL-3.0-or-later",
"require": {
"php": "^7.4 || ^8.0",
"contao/core-bundle": "^4.9",
"contao/core-bundle": "^4.13",
"ext-json": "*",
"symfony/cache": "^4.4||^5.4",
"symfony/config": "^4.4||^5.4",
Expand Down
153 changes: 118 additions & 35 deletions src/Filesystem/TwigTemplateLocator.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,23 @@
namespace HeimrichHannot\TwigSupportBundle\Filesystem;

use Contao\CoreBundle\Config\ResourceFinderInterface;
use Contao\CoreBundle\ContaoCoreBundle;
use Contao\CoreBundle\Framework\ContaoFramework;
use Contao\CoreBundle\Routing\ScopeMatcher;
use Contao\CoreBundle\Twig\Loader\TemplateLocator;
use Contao\PageModel;
use Contao\ThemeModel;
use Contao\Validator;
use HeimrichHannot\TwigSupportBundle\Cache\TemplateCache;
use HeimrichHannot\TwigSupportBundle\Exception\TemplateNotFoundException;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Symfony\Component\Filesystem\Path;
use Symfony\Component\Finder\Exception\DirectoryNotFoundException;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Stopwatch\Stopwatch;
use Webmozart\PathUtil\Path;

class TwigTemplateLocator
{
Expand All @@ -39,8 +38,18 @@ class TwigTemplateLocator
protected Stopwatch $stopwatch;
protected FilesystemAdapter $templateCache;
private ContaoFramework $contaoFramework;

public function __construct(KernelInterface $kernel, ResourceFinderInterface $contaoResourceFinder, RequestStack $requestStack, ScopeMatcher $scopeMatcher, Stopwatch $stopwatch, FilesystemAdapter $templateCache, ContaoFramework $contaoFramework)
private TemplateLocator $templateLocator;

public function __construct(
KernelInterface $kernel,
ResourceFinderInterface $contaoResourceFinder,
RequestStack $requestStack,
ScopeMatcher $scopeMatcher,
Stopwatch $stopwatch,
FilesystemAdapter $templateCache,
ContaoFramework $contaoFramework,
TemplateLocator $templateLocator
)
{
$this->kernel = $kernel;
$this->contaoResourceFinder = $contaoResourceFinder;
Expand All @@ -49,6 +58,7 @@ public function __construct(KernelInterface $kernel, ResourceFinderInterface $co
$this->stopwatch = $stopwatch;
$this->templateCache = $templateCache;
$this->contaoFramework = $contaoFramework;
$this->templateLocator = $templateLocator;
}

/**
Expand Down Expand Up @@ -83,7 +93,7 @@ public function getTemplateContext(string $templateName, array $options = []): T
/* @var PageModel $objPage */
global $objPage;

if ('' != $objPage->templateGroup) {
if ($objPage && '' != $objPage->templateGroup) {
if (Validator::isInsecurePath($objPage->templateGroup)) {
throw new \RuntimeException('Invalid path '.$objPage->templateGroup);
}
Expand All @@ -107,7 +117,7 @@ public function getTemplateContext(string $templateName, array $options = []): T
$pathLength = \strlen($themeFolder);

foreach ($template['paths'] as $path) {
if ($themeFolder === substr($path, 0, $pathLength)) {
if (str_starts_with($path, '@Contao_Theme_'.$themeFolder)) {
return new TemplateContext($templateName, $path, $template['pathInfo'][$path]);
}
}
Expand All @@ -119,9 +129,15 @@ public function getTemplateContext(string $templateName, array $options = []): T
}
}

$path = end($template['paths']);
$key = array_key_last($template['paths']);
while (isset($template['paths'][$key])) {
if (!str_starts_with($template['paths'][$key], '@Contao_Theme_')) {
return new TemplateContext($templateName, $template['paths'][$key], $template['pathInfo'][$template['paths'][$key]]);
}
$key--;
}

return new TemplateContext($templateName, $path, $template['pathInfo'][$path]);
throw new TemplateNotFoundException(sprintf('Unable to find template "%s".', $templateName));
}

/**
Expand Down Expand Up @@ -278,7 +294,8 @@ public function getTemplates(bool $extension = false, bool $disableCache = false
$cacheItem->set($this->generateContaoTwigTemplatePaths($extension));
$this->templateCache->save($cacheItem);
}
$cachedTemplates = $this->templateCache->getItem($cacheKey)->get();

$cachedTemplates = $cacheItem->get();

if (!\is_array($cachedTemplates)) {
// clean invalid cache entry
Expand Down Expand Up @@ -342,6 +359,9 @@ public function getTwigTemplatesInPath($dir, ?string $twigKey = null, bool $exte
* - extension: (bool) Add extension to filename (array key)
*
* @param iterable|string $dir
*
* @deprecated Use Contao\CoreBundle\Twig\Loader\TemplateLocator::findTemplates()
* @codeCoverageIgnore
*/
public function getTemplatesInPath($dir, ?BundleInterface $bundle = null, array $options = []): array
{
Expand Down Expand Up @@ -407,9 +427,14 @@ public function getTemplatesInPath($dir, ?BundleInterface $bundle = null, array
*/
protected function generateContaoTwigTemplatePaths(bool $extension = false): array
{
$stopwatchname = 'TwigTemplateLocator::generateContaoTwigTemplatePaths()';
$this->stopwatch->start($stopwatchname);

$contaoResourcePaths = $this->templateLocator->findResourcesPaths();
$contaoThemePaths = $this->templateLocator->findThemeDirectories();
$bundles = $this->kernel->getBundles();
$twigFiles = [];

$resourcePaths = [];
if (\is_array($bundles)) {
foreach ($bundles as $key => $bundle) {
$path = $bundle->getPath();
Expand All @@ -419,37 +444,95 @@ protected function generateContaoTwigTemplatePaths(bool $extension = false): arr
continue;
}

$twigFiles = array_merge_recursive($twigFiles, $this->getTemplatesInPath($dir, $bundle, ['extension' => $extension]));
$resourcePaths[$key][] = $dir;
}
if (isset($contaoResourcePaths[$key])) {
$resourcePaths[$key] = array_merge(($resourcePaths[$key] ?? []), $contaoResourcePaths[$key]);
}
}
}

$bundle = null;
if (version_compare(\VERSION, '4.12', '>=')) {
$bundle = new class extends Bundle {
public function __construct()
{
$this->name = 'Contao';
}
};
if (isset($contaoResourcePaths['App'])) {
$resourcePaths['App'] = $contaoResourcePaths['App'];
if (!in_array($this->kernel->getProjectDir().'/templates', $resourcePaths['App'])) {
$resourcePaths['App'][] = $this->kernel->getProjectDir().'/templates';
}
}

// Bundle template folders
$twigFiles = array_merge_recursive($twigFiles, $this->getTemplatesInPath(
$this->contaoResourceFinder->findIn('templates')->name('*.twig')->getIterator(),
$bundle,
['extension' => $extension]));

// Project template folders
$twigFiles = array_merge_recursive(
$twigFiles,
$this->getTemplatesInPath($this->kernel->getProjectDir().'/contao/templates', $bundle, ['extension' => $extension])
);
$twigFiles = array_merge_recursive(
$twigFiles,
$this->getTemplatesInPath($this->kernel->getProjectDir().'/templates', null, ['extension' => $extension])
);
$twigFiles = [];
foreach ($resourcePaths as $bundle => $paths) {
foreach ($paths as $path) {
$path = Path::canonicalize($path);
$templates = $this->templateLocator->findTemplates($path);
if (empty($templates)) {
continue;
}

if ('App' === $bundle) {
if (str_contains($path, '/contao/templates')) {
$namespace = 'Contao_App';
} else {
$namespace = '';
}
} else {
if (str_contains($path, '/contao/templates')) {
$namespace = 'Contao_'.$bundle;
} else {
$namespace = preg_replace('/Bundle$/', '', $bundle);
}
}

foreach ($templates as $name => $templatePath) {
if (str_ends_with($name, '.html5')) {
continue;
}

$prefix = $namespace;

if (empty($namespace) && str_contains($name, '/')) {
$parts = explode('/', $name);
if (isset($contaoThemePaths[$parts[0]])
&& (Path::getLongestCommonBasePath($contaoThemePaths[$parts[0]], $templatePath)) === $contaoThemePaths[$parts[0]]) {
$prefix = 'Contao_Theme_'.$parts[0];
$name = Path::makeRelative($templatePath, $contaoThemePaths[$parts[0]]);
}
}

$twigPath = ($prefix ? "@$prefix/" : '').$name;

if (!$extension) {
if (str_ends_with($name, '.html.twig')) {
$name = substr($name, 0, -10);
}
}

$this->addPath($twigFiles, $name, $twigPath, $bundle, $path);

// check for modern contao template paths and legacy fallback
if (!str_contains($name, '/')) {
continue;
}

$file = new \SplFileInfo($templatePath);
$name = $file->getBasename();
if (str_ends_with($name, '.html.twig')) {
$name = substr($name, 0, -10);
}

$this->addPath($twigFiles, $name, $twigPath, $bundle, $path, true);
}
}
}

$this->stopwatch->stop($stopwatchname);
return $twigFiles;
}

private function addPath(array &$pathData, string $name, string $twigPath, ?string $bundleName, string $absolutePath, bool $deprecated = false): void
{
$pathData[$name]['paths'][] = $twigPath;
$pathData[$name]['pathInfo'][$twigPath]['bundle'] = $bundleName;
$pathData[$name]['pathInfo'][$twigPath]['pathname'] = $absolutePath;
$pathData[$name]['pathInfo'][$twigPath]['deprecatedPath'] = $deprecated;
}
}
Loading

0 comments on commit 5425f89

Please sign in to comment.