<?php declare(strict_types=1);
namespace CogiGlossary\Subscriber;
use Composer\IO\NullIO;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\ParameterType;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\Struct\ArrayEntity;
use Shopware\Core\System\SystemConfig\SystemConfigService;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityLoadedEvent;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\Filter;
use Shopware\Core\Content\Cms\CmsPageEvents;
use Shopware\Core\Framework\Struct\Struct;
use Shopware\Core\Content\Seo\SeoUrlPlaceholderHandlerInterface;
use Shopware\Storefront\Page\Product\ProductPageLoadedEvent;
use Shopware\Core\Content\Cms\Events\CmsPageLoadedEvent;
use Shopware\Core\Framework\Api\Context\SalesChannelApiSource;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Grouping\FieldGrouping;
class StorefrontSubscriber implements EventSubscriberInterface
{
private $systemConfigService;
private $mediaRepository;
private $connection;
/**
* @var SeoUrlPlaceholderHandlerInterface
*/
private $seoUrlReplacer;
/**
* @var EntityRepositoryInterface
*/
private $repository;
public function __construct(
SystemConfigService $systemConfigService,
SeoUrlPlaceholderHandlerInterface $seoUrlReplacer,
EntityRepositoryInterface $repository,
Connection $connection
)
{
$this->systemConfigService = $systemConfigService;
$this->seoUrlReplacer = $seoUrlReplacer;
$this->repository = $repository;
$this->connection = $connection;
}
public static function getSubscribedEvents(): array
{
return [
CmsPageEvents::SLOT_LOADED_EVENT => 'onEntityLoadedEvent',
ProductPageLoadedEvent::class => 'onProductPageLoadedEvent'
];
}
public function onProductPageLoadedEvent(ProductPageLoadedEvent $event): void
{
if (!$this->systemConfigService->get('CogiGlossary.config.highlight')) {
return;
}
$replace = $this->prepareGlossary($event);
$product = $event->getPage()->getProduct()->getTranslated();
$product['description'] = str_replace($replace['from'], $replace['to'], $product['description']);
$event->getPage()->getProduct()->setTranslated($product);
}
public function onEntityLoadedEvent(EntityLoadedEvent $event): void
{
if (!$this->systemConfigService->get('CogiGlossary.config.highlight')) {
return;
}
$source = $event->getContext()->getSource();
if (!$source instanceof SalesChannelApiSource) {
return;
}
$replace = $this->prepareGlossary($event);
$entities = $event->getEntities();
foreach ($entities as $entity) {
if ($entity->getType() == 'text') {
$config = $entity->getTranslated();
foreach ($config['config'] as &$item) {
if ($item['value'] && $this->isHTML($item['value'])) {
$dom = new \DOMDocument();
@$dom->loadHTML(mb_convert_encoding($item['value'], 'HTML-ENTITIES', 'UTF-8'));
$preparedHTML = $dom->saveHTML($this->nodeReplace($replace['from'], $replace['node'], $dom));
$item['value'] = str_replace($replace['node'], $replace['to'], $preparedHTML);
}
}
$entity->setTranslated($config);
}
}
}
private function prepareGlossary($event)
{
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('active', 1));
$criteria->addAssociation('media');
$replaceFrom = [];
$replaceNode = [];
$replaceTo = [];
$cogiGlossary = $this->repository->search($criteria, $event->getContext())->getEntities();
foreach ($cogiGlossary as $item) {
$replaceFrom[] = $item->getName();
$replaceNode[] = '###' . $item->getName() . '###';
if ($this->systemConfigService->get('CogiGlossary.config.directUrl')) {
$url = $this->seoUrlReplacer->generate('cogi-glossary.article', ['firstChar' => $item->getFirstChar(), 'glossaryId' => $item->getId()]);
} else {
$url = $this->seoUrlReplacer->generate('cogi-glossary.index', ['firstChar' => $item->getFirstChar()]) . '#' . $item->getName();
}
if ($this->systemConfigService->get('CogiGlossary.config.hovercard')) {
$replaceTo[] = '<a class="glossary-link" title="' . $item->getName() . '" data-cogi-glossary-hover="' . $this->seoUrlReplacer->generate('cogi-glossary.hover', ['glossaryId' => $item->getId()]) . '" href="' .$url.'">' . $item->getName() . '</a>';
} else {
$replaceTo[] = '<a class="glossary-link" title="' . $item->getName() . '" href="' .$url.'">' . $item->getName() . '</a>';
}
}
return [
'from' => $replaceFrom,
'node' => $replaceNode,
'to' => $replaceTo
];
}
private function nodeReplace($replaceFrom, $replaceTo, $node)
{
if ($node instanceof \DOMElement) {
if (in_array($node->nodeName, ['a', 'button', 'h1', 'h2', 'h3', 'h4', 'h5'])) {
return $node;
}
}
if ($node instanceof \DOMText) {
$node->data = str_replace($replaceFrom, $replaceTo, $node->data);
}
if ($node->childNodes) {
foreach ($node->childNodes AS $child) {
$this->nodeReplace($replaceFrom, $replaceTo, $child);
}
}
return $node;
}
private function isHTML($string)
{
if(!is_string($string)){
return false;
}
return $string != strip_tags($string) ? true : false;
}
}