<?php
namespace Boldr\Cms\CmsBundle\Controller;
use Boldr\Cms\CmsBundle\Controller\PermalinkController;
use Boldr\Cms\CmsBundle\Permalink\PermalinkableInterface;
use Psr\Cache\CacheItemInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\{ Request, Response, ResponseHeaderBag};
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Contracts\Cache\CacheInterface;
class SitemapController extends AbstractController
{
private iterable $sitemapLinkProviders;
private PermalinkController $permalinkController;
public function __construct(iterable $sitemapLinkProviders, PermalinkController $permalinkController)
{
$this->sitemapLinkProviders = $sitemapLinkProviders;
$this->permalinkController = $permalinkController;
}
/** @Route("/robots.txt", name="cms_robots") */
public function robots()
{
$response = new Response('Sitemap: '. $this->generateUrl('cms_sitemap', [], UrlGeneratorInterface::ABSOLUTE_URL) . PHP_EOL);
$response->headers->set('Content-Type', 'text/plain');
return $response;
}
/**
* @Route("/sitemap.xml", name="cms_sitemap")
*/
public function sitemap(Request $request, CacheInterface $cache)
{
$base = $request->getSchemeAndHttpHost().$request->getBaseUrl();
$hash = md5($base);
$cachePath = $this->getParameter('kernel.cache_dir').'/boldrcms-sitemap-'.$hash.'.xml';
if (!file_exists($cachePath) || time() - filemtime($cachePath) > 3600)
{
$this->generateSitemap($base, $cachePath);
}
$response = $this->file($cachePath, 'sitemap.xml', ResponseHeaderBag::DISPOSITION_INLINE);
$response->headers->set('Content-Type', 'application/xml');
return $response;
}
private function generateSitemap(string $base, string $path): void
{
$xml = '<?xml version="1.0" encoding="UTF-8"?>';
$xml .= '<?xml-stylesheet type="text/xsl" href="/bundles/boldrcms/sitemap.xsl"?>';
$xml .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">';
$localeBaseUrls = $this->getParameter('boldr_cms.locale_base_urls');
$currentLocale = false;
foreach ($localeBaseUrls as $locale => $localeBaseUrl)
{
if (is_array($localeBaseUrl) ? in_array($base, $localeBaseUrl, true) : $localeBaseUrl === $base)
{
$currentLocale = $locale;
break;
}
}
$currentLocale = $currentLocale === false ? $this->getParameter('kernel.default_locale') : $currentLocale;
foreach ($this->sitemapLinkProviders as $sitemapLinkProvider)
{
foreach ($sitemapLinkProvider->getSitemapLinks() as $link)
{
$priority = $link->getPriority();
$lastModified = $link->getLastModified();
$changeFrequency = $link->getChangeFrequency();
$url = $link->getUrl();
$urlsXml = '';
if ($url instanceof PermalinkableInterface)
{
$urls = [];
foreach ($this->getParameter('boldr_cms.enabled_locales') as $locale) {
$permalink = $this->permalinkController->generate($url, $locale);
if ($permalink)
{
$urls[$locale] = $permalink;
}
}
$url = $urls;
}
if (is_array($url))
{
foreach ($url as $locale => $localizedUrl)
{
$localizedBase = $localeBaseUrls[$locale] ?? $base;
if (is_array($localizedBase))
{
$localizedBase = $localizedBase[0];
}
if ($locale !== $currentLocale)
{
$urlsXml .= '<xhtml:link rel="alternate" hreflang="'.self::encodeString($locale).'" href="'.self::encodeString($localizedBase.$localizedUrl).'"/>';
}
}
$defaultUrl = $url[$currentLocale] ?? current($url);
}
else
{
$defaultUrl = $url;
}
$xml .= '<url>'.
'<loc>'.self::encodeString($base.$defaultUrl).'</loc>'.
($lastModified !== null
? '<lastmod>'.self::encodeString($link->getLastModified()->format('Y-m-d\TH:i:s+00:00')).'</lastmod>'
: ''
).
$urlsXml.
($changeFrequency !== null
? '<changefreq>'.self::encodeString($link->getChangeFrequency()).'</changefreq>'
: ''
).
($priority != 0.5
? '<priority>'.$link->getPriority().'</priority>'
: ''
).
'</url>'.PHP_EOL;
}
}
$xml .= '</urlset>';
file_put_contents($path, $xml);
}
private static function encodeString(string $str): string
{
return strtr($str, [
'<' => '<',
'&' => '&',
'>' => '>',
'"' => '"',
'\'' => '''
]);
}
}