<?php
namespace App\Renderer;
use App\Component\Configuration\Util\Config;
use App\Entity\Asset;
use App\Entity\HeroConfiguration;
use App\Entity\OgImage;
use App\Entity\Page as PageEntity;
use App\Entity\Review;
use App\Entity\Route;
use App\Entity\Taggable;
use App\Event\PageEvent;
use App\EventListener\FeatureFlagListener;
use App\Model\Tealium\Base as TealiumBase;
use App\Util\Breadcrumb;
use App\Util\BreadcrumbableInterface;
use App\Util\CookiescriptHelper;
use App\Util\Seo;
use Doctrine\ORM\EntityManagerInterface;
use Flagception\Manager\FeatureManagerInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Twig\Environment as Twig_Environment;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Error\SyntaxError;
class Page
{
private EntityManagerInterface $entityManager;
private Twig_Environment $twig;
private Breadcrumb $breadcrumbUtil;
private ?Request $request;
protected Seo $seoUtil;
private FeatureManagerInterface $featureManager;
private ParameterBagInterface $parameterBag;
private EventDispatcherInterface $eventDispatcher;
private ?object $entity = null;
private Config $config;
/**
* Page constructor.
*/
public function __construct(
EntityManagerInterface $entityManager,
Twig_Environment $twig,
RequestStack $requestStack,
Breadcrumb $breadcrumb,
Seo $seoUtil,
FeatureManagerInterface $featureManager,
ParameterBagInterface $parameterBag,
EventDispatcherInterface $eventDispatcher,
Config $config,
) {
$this->entityManager = $entityManager;
$this->twig = $twig;
$this->breadcrumbUtil = $breadcrumb;
$this->request = $requestStack->getCurrentRequest();
$this->seoUtil = $seoUtil;
$this->featureManager = $featureManager;
$this->parameterBag = $parameterBag;
$this->eventDispatcher = $eventDispatcher;
$this->config = $config;
}
public function renderSystemPage(PageEntity $page): Response
{
$content = $this->twig->render('@default/pages/pages_index.html.twig', [
'header' => $this->getHeader($page),
'footer' => $this->getFooter(),
'page' => $page,
]);
return new Response($content);
}
/**
* @param Asset|HeroConfiguration|string|null $hero
*
* @throws LoaderError
* @throws RuntimeError
* @throws SyntaxError
*/
public function renderPage(
string $placeholder,
string $template,
array $data,
array $canonicals = [],
array $metaData = [],
?BreadcrumbableInterface $breadcrumbEntity = null,
?Route $route = null,
mixed $hero = null,
array $options = []
): Response {
if (!$route) {
$route = $this->entityManager
->getRepository(Route::class)
->findOneBy(['name' => $this->request->get('_route')])
;
}
// if route is not found, throw NotFoundHttpException
if (!$route) {
throw new NotFoundHttpException('Page not found');
}
/** @var PageEntity $page */
$page = $route->getPage();
if ($page->getFeature() && !$this->featureManager->isActive($page->getFeature())) {
throw new NotFoundHttpException();
}
$page->setBreadcrumbEntity($breadcrumbEntity);
$breadcrumbs = $this->breadcrumbUtil->getBreadcrumbsForPage($page, $breadcrumbEntity);
if ($breadcrumbEntity && $breadcrumbEntity->getBreadcrumbTitle()) {
$page->setTitle($breadcrumbEntity->getBreadcrumbTitle());
}
$defaultOgImage = null;
if ($ogImage = $this->config->get('site_open_graph_image')) {
$defaultOgImage = (new OgImage())->setImage($ogImage);
}
$this->seoUtil->setSeoInformationForPage(
$metaData,
$page,
$defaultOgImage
);
if ($hero instanceof Asset) {
$hero = $this->twig->render('@default/pages/hero.html.twig', ['hero' => $hero]);
} elseif ($hero instanceof HeroConfiguration) {
$hero = $this->twig->render('@default/pages/hero_configuration.html.twig', [
'heroConfiguration' => $hero,
]);
} elseif ($page->getHeroConfiguration() instanceof HeroConfiguration) {
$hero = $this->twig->render('@default/pages/hero_configuration.html.twig', [
'heroConfiguration' => $page->getHeroConfiguration(),
]);
} elseif (\is_string($hero) && '' !== $hero) {
$hero = $this->twig->render('@default/pages/hero_as_string.html.twig', ['hero' => $hero]);
}
$this->eventDispatcher->dispatch(
new PageEvent($page, $this->entity, $metaData, $this->request),
PageEvent::PAGE_BEFORE_RENDER
);
$scripts = '';
if (\array_key_exists('jobPostingStructuredData', $data)
&& \is_string($data['jobPostingStructuredData'])
&& '' !== $data['jobPostingStructuredData']
) {
$scripts .= $this->twig->render(
'page/vacancy/job_posting_structered_data.html.twig',
['jobPostingStructuredData' => $data['jobPostingStructuredData']]
);
}
$tealium = TealiumBase::createForPage($this->request, $page, $route);
if ($this->entity) {
$tealium->removeProperty('page_id')
->addProperty('page_id', sprintf('%s_%s', $route->getName(), $this->entity->getId()))
;
if ($this->entity instanceof Taggable && $this->entity->getTags()) {
$tealium->removeProperty('page_tags')
->addProperty('page_tags', array_map('strval', $this->entity->getTags()->toArray()))
;
}
}
if (\array_key_exists('tealium_element', $options) && $options['tealium_element'] instanceof TealiumBase) {
$tealium->merge($options['tealium_element']);
}
if ($this->featureManager->isActive(FeatureFlagListener::FEATURE_THEME_INHERITANCE)) {
$content = $this->twig->render($template, array_merge($data, [
'header' => $this->getHeader($page),
'footer' => $this->getFooter(),
'page' => $page,
'breadcrumbs' => $breadcrumbs,
'canonicals' => $canonicals,
'hero' => $hero,
'scripts' => $scripts,
'tealium_data' => $tealium->toJson(),
'tealium_push_directly' => !\array_key_exists('skip_tealium', $options) || !$options['skip_tealium'],
]));
$response = new Response($content);
} else {
$body = str_replace(
$placeholder,
$this->twig->render(
$template,
array_merge($data, [
'page' => $page,
'breadcrumbs' => $breadcrumbs,
])
),
$page->getBody()
);
$page->setBody($body);
$this->entityManager->clear(PageEntity::class);
$response = new Response($this->twig->render(
'@default/pages/pages_index.html.twig',
array_merge($data, [
'header' => $this->getHeader($page),
'footer' => $this->getFooter(),
'page' => $page,
'canonicals' => $canonicals,
'breadcrumbs' => $breadcrumbs,
'hero' => $hero,
'scripts' => $scripts,
])
));
}
$response->setContent(
CookiescriptHelper::cookiefyMarkup($response->getContent())
);
$this->eventDispatcher->dispatch(
new ResponseCreatedByPageRenderer($page, $this->entity, $response)
);
$this->entity = null;
return $response;
}
public function getHeader(?PageEntity $activePage = null): string
{
$pageRepository = $this->entityManager->getRepository(PageEntity::class);
if ($this->parameterBag->get('site_translation_multiple_locales')) {
$pageRepository->setMultilingual(true);
}
$reviews = [];
$scores = [];
if ($this->featureManager->isActive(FeatureFlagListener::FEATURE_REVIEW)) {
$reviews = $this->entityManager->getRepository(Review::class)->findAll();
$scores = array_map(static function (Review $review) {
return $review->getScore();
}, $reviews);
}
$averageScore = 0;
if ($reviews && $scores) {
$averageScore = array_sum($scores) / \count($reviews);
}
try {
$response = $this->twig->render('@default/pages/pages_header.html.twig', [
'pages' => $pageRepository->getHeaderNavigation(),
'topNavigation' => $pageRepository->getTopNavigation(),
'averageScore' => $averageScore,
'reviews' => $reviews,
'activePage' => $activePage,
]);
} catch (\Exception) {
$response = '';
}
return $response;
}
public function getFooter(): string
{
$pageRepository = $this->entityManager->getRepository(PageEntity::class);
if ($this->parameterBag->get('site_translation_multiple_locales')) {
$pageRepository->setMultilingual(true);
}
try {
$response = $this->twig->render(
'@default/pages/pages_footer.html.twig',
[
'pages' => $pageRepository->getFooterNavigation(),
'footer_bottom_pages' => $pageRepository->getFooterBottomNavigation(),
'header_pages' => $pageRepository->getHeaderNavigation(),
]
);
} catch (\Exception) {
$response = '';
}
return $response;
}
public function withEntity(object $entity): self
{
$this->entity = $entity;
return $this;
}
}