<?php
namespace Elements\Bundle\JsonLdReloadedBundle\Twig;
use Elements\Bundle\JsonLdReloadedBundle\JsonLd\TemplateRenderer;
use Pimcore\Db;
use Pimcore\Model\DataObject\AbstractObject;
use Pimcore\Model\Document;
use Pimcore\Tool;
use Pimcore\Log\ApplicationLogger;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Twig\Environment;
use Twig\Extension\AbstractExtension;
use Twig\Markup;
use Twig\TwigFunction;
class JsonLdExtension extends AbstractExtension
{
/**
* @var Environment
*/
private $twig;
/**
* @var Request|null
*/
private $request;
/**
* @var TemplateRenderer
*/
private $templateRenderer;
/**
* @var TemplateRenderer
*/
private $disableDocumentOutput;
/**
* @var ApplicationLogger
*/
private $logger;
const COMPONENT_NAME = 'ElementsJsonLdReloadedBundle';
/**
* Extension constructor.
* @param Environment $twig
* @param RequestStack $requestStack
* @param TemplateRenderer $templateRenderer
*/
public function __construct(Environment $twig, RequestStack $requestStack, TemplateRenderer $templateRenderer, ApplicationLogger $logger)
{
$this->twig = $twig;
$this->request = $requestStack->getMasterRequest();
$this->templateRenderer = $templateRenderer;
$this->disableDocumentOutput = false;
$this->logger = $logger;
}
public function getFunctions()
{
return [
new TwigFunction('jld_root', [$this, 'jsonLdReloadedRoot'], ['is_safe' => ['html']]),
new TwigFunction('jld_document', [$this, 'jsonLdReloadedRootDocument'], ['is_safe' => ['html']]),
new TwigFunction('jld', [$this, 'jsonLdReloaded'], ['is_safe' => ['html']]),
new TwigFunction('jld_disableDocumentOutput', [$this, 'setDisableDocumentOutput'], ['is_safe' => ['html']]),
];
}
public function jsonLdReloadedRoot($object)
{
return new Markup(
sprintf('<script type="application/ld+json">%s</script>', $this->jsonLdReloaded($object, true)),
'UTF-8'
);
}
public function jsonLdReloadedRootDocument($document)
{
$documentProperties = $document->getProperties();
$possibleProperties = $this->getJsonLdProperties();
$jsonLdString = '';
if (!$this->disableDocumentOutput) {
$isStaticRoute = $this->request?->get('pimcore_request_source') === 'staticroute';
foreach ($possibleProperties as $propertyName) {
if ($isStaticRoute && array_key_exists($propertyName, $documentProperties) && !$documentProperties[$propertyName]->getInheritable()) {
continue;
}
if (intval($document->getProperty($propertyName))) {
if ($templateString = $this->getJsonLdByTemplateId(intval($document->getProperty($propertyName)), $document)) {
$jsonLdString .= sprintf('<script type="application/ld+json">%s</script>', $templateString);
}
}
}
}
return new Markup(
$jsonLdString,
'UTF-8'
);
}
/**
* @param $object
* @param bool $isRoot - wraps jsonld in script tag
* @param bool $prettyPrint - nicely formats the jsonld script
* @return Markup
*/
public function jsonLdReloaded($object, bool $isRoot = false, bool $prettyPrint = false, $showErrors = false)
{
$canInherit = AbstractObject::getGetInheritedValues();
AbstractObject::setGetInheritedValues(true);
try {
$jsonLd = $this->templateRenderer->render($object, [
'__path' => $this->request ? $this->request->getPathInfo() : '',
'__url' => Tool::getHostUrl() . ($this->request ? $this->request->getRequestUri() : ''),
'isRoot' => $isRoot
]);
} catch (\Throwable $exception) {
// $this->logger->error($exception->getMessage() . ": " . $exception->getTraceAsString() . ': ' . $exception, ['component' => self::COMPONENT_NAME]);
// Site should not die because of jsonLD
$jsonLd = '{}';
if($showErrors) {
$jsonLd = '<div class="alert alert-danger">Error while processing JsonLd: <br>' . $exception->getMessage() . '</div>';
}
} finally {
AbstractObject::setGetInheritedValues($canInherit);
}
if ($prettyPrint === true) {
$json = json_decode($jsonLd);
if ($json !== null) {
$jsonLd = json_encode($json, JSON_PRETTY_PRINT);
} elseif($showErrors) { //json not valid
$jsonLd = '<div class="alert alert-danger">Json not valid: <br>' . json_last_error_msg() . '</div><br>' . $jsonLd;
}
}
return new Markup($jsonLd, 'UTF-8');
}
/**
* @param $templateId
* @param Document $document
* @return Markup
*/
private function getJsonLdByTemplateId($templateId, Document $document) {
try {
$jsonLd = $this->templateRenderer->renderTemplate($templateId, [
'__path' => $this->request ? $this->request->getPathInfo() : '',
'__url' => Tool::getHostUrl() . ($this->request ? $this->request->getRequestUri() : ''),
'object' => $document,
'isRoot' => true
]);
} catch (\Exception $exception) {
$this->logger->error($exception->getMessage() . ": " . $exception->getTraceAsString() . ': ' . $exception, ['component' => self::COMPONENT_NAME]);
// Site should not die because of jsonLD
$jsonLd = '{}';
}
return new Markup($jsonLd, 'UTF-8');
}
private $jsonLDProperties = [];
private function getJsonLdProperties() {
if (empty($this->jsonLDProperties)) {
$db = Db::get();
$this->jsonLDProperties = $db->fetchCol('SELECT name FROM properties WHERE ctype = "document" AND name LIKE "json-ld-%" GROUP BY name');
}
return $this->jsonLDProperties;
}
/**
* @param bool $switch
*/
public function setDisableDocumentOutput(bool $switch) {
$this->disableDocumentOutput = $switch;
}
}