<?php
namespace Boldr\Cms\CmsBundle\Controller;
use Boldr\Cms\CmsBundle\Permalink\{ PermalinkHandlerInterface, PermalinkResolverInterface, PermalinkGeneratorInterface, PermalinkableInterface };
use Boldr\Cms\CmsBundle\Homepage\HomepageProviderInterface;
use Boldr\Cms\CmsBundle\Entity\{ Page, PageTranslation };
use Boldr\Cms\CmsBundle\Content\Assets;
use Boldr\Cms\CmsBundle\Upload\UploadStorageManager;
use Boldr\Cms\CmsBundle\Presenter\Page\PagePresenter;
use Boldr\Cms\CmsBundle\Repository\PageRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\{ Request, Response, RequestStack };
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Contracts\Cache\TagAwareCacheInterface;
class PageController extends AbstractController implements PermalinkHandlerInterface, PermalinkResolverInterface, PermalinkGeneratorInterface, HomepageProviderInterface
{
private PagePresenter $pagePresenter;
private PageRepository $pageRepository;
private UploadStorageManager $uploadStorageManager;
private TagAwareCacheInterface $cache;
public function __construct(PagePresenter $pagePresenter, PageRepository $pageRepository, UploadStorageManager $uploadStorageManager, TagAwareCacheInterface $cache)
{
$this->pagePresenter = $pagePresenter;
$this->pageRepository = $pageRepository;
$this->uploadStorageManager = $uploadStorageManager;
$this->cache = $cache;
}
/**
* @param Page $permalinkable
*/
public function handlePermalink(PermalinkableInterface $permalinkable, Request $request): Response
{
return $this->renderPage($request, $permalinkable);
}
public function resolvePermalink(string $permalink, string $locale): ?PermalinkableInterface
{
$repoPageTranslations = $this->getDoctrine()->getRepository(PageTranslation::class);
$parts = explode('/', $permalink);
$page = null;
foreach ($parts as $part)
{
$qb = $this->pageRepository->createQueryBuilder('p')
->leftJoin('p.translations', 'pt')
->andWhere('pt.locale = :locale')
->andWhere('pt.permalink = :part')
->andWhere('p.parent '. ($page === null ? 'IS NULL' : '= :parent'))
->setParameter('locale', $locale)
->setParameter('part', $part)
->setMaxResults(1)
;
if ($page !== null)
{
$qb->setParameter('parent', $page);
}
$page = $qb->getQuery()->getOneOrNullResult();
// If no page with this permalink can be found, return null
if ($page === null)
{
return null;
}
// Otherwise continue checking for "sub" pages if $parts remain
}
return $page;
}
/**
* @param Page $permalinkable
*/
public function generatePermalink(PermalinkableInterface $permalinkable, string $locale): string
{
$permalinkParts = [];
$page = $permalinkable;
while ($page !== null)
{
$translation = $page->getTranslations()->get($locale);
if ($translation->getPermalink() === '' || $translation->getSections() === null)
{
return '';
}
array_unshift($permalinkParts, $translation->getPermalink());
$page = $page->getParent();
}
return join('/', $permalinkParts);
}
public function renderPage(Request $request, Page $page, bool $preview = false): Response
{
$locale = $request->getLocale();
$translation = $page->getTranslations()->get($locale);
if ($translation->getSections() === null && !$preview)
{
throw $this->createNotFoundException();
}
$pageView = $this->pagePresenter->createPageView($page, $locale, $preview);
// JSON:
if (in_array('application/json', $request->getAcceptableContentTypes()))
{
return $this->json($pageView);
}
// HTML:
foreach ($pageView->getSections() as $section)
{
$section->getAssets();
}
$assets = new Assets();
$assets->addAll(...array_map(
fn($section) => $section->getAssets(),
$pageView->getSections()
));
// Add OEmbed/OpenGraph meta tags
$metaTagsCacheItem = $this->cache->getItem('boldr_cms.page.meta_tags.'. $page->getId().'.'.$locale);
if (!$metaTagsCacheItem->isHit() || $preview)
{
$metaTags = [
'<meta name="twitter:card" content="'. ($page->getPrimaryImage() ? 'summary_large_image' : 'summary') .'" />',
'<meta property="og:url" content="'. $request->getUri() .'" />',
'<meta property="og:type" content="article" />'
];
if ($pageView->getDescription())
{
$metaTags[] = '<meta name="description" property="og:description" content="'.htmlentities($pageView->getDescription()).'" />';
}
if ($page->getPrimaryImage())
{
$baseUrl = $request->getSchemeAndHttpHost().$request->getBasePath();
$imageUrl = $this->uploadStorageManager->getImageUrl($page->getPrimaryImage(), $baseUrl, 'medium');
$metaTags[] = '<meta property="og:image" content="'. $imageUrl .'" />';
}
$metaTagsHtml = join("\n", $metaTags);
if (!$preview)
{
$metaTagsCacheItem->set($metaTagsHtml);
$metaTagsCacheItem->tag('boldr_cms.page.'. $page->getId());
$this->cache->saveDeferred($metaTagsCacheItem);
}
}
else
{
$metaTagsHtml = $metaTagsCacheItem->get();
}
$assets->addHtmlContentToHead($metaTagsHtml);
$template = $pageView->getPageLayout()->getTemplate();
$variables = $pageView->getPageLayout()->getVariables();
return $this->render($template, [
'page' => $pageView,
'assets' => $assets,
] + $variables);
}
public function handleHomepage(Request $request, string $homepageId): Response
{
$repoPage = $this->getDoctrine()->getRepository(Page::class);
$page = $repoPage->createQueryBuilder('p')
->select('p, t')
->leftJoin('p.translations', 't')
->where('p.id = ?1')
->setParameter(1, $homepageId)
->getQuery()
->getOneOrNullResult();
if ($page === null)
{
throw new \Exception('Homepage misconfigured');
}
$locale = $request->getLocale();
// Save the permalinkable in a request attribute
$request->attributes->set('_permalinkable', $page);
return $this->renderPage($request, $page);
}
}