custom/plugins/SasBlogModule/src/SasBlogModule.php line 31

  1. <?php declare(strict_types=1);
  2. namespace Sas\BlogModule;
  3. use Doctrine\DBAL\Connection;
  4. use Doctrine\DBAL\Driver\Result;
  5. use Sas\BlogModule\Content\Blog\BlogEntriesDefinition;
  6. use Sas\BlogModule\Content\Blog\BlogSeoUrlRoute;
  7. use Sas\BlogModule\Content\Blog\Events\BlogIndexerEvent;
  8. use Sas\BlogModule\Util\Lifecycle;
  9. use Sas\BlogModule\Util\Update;
  10. use Shopware\Core\Content\Media\Aggregate\MediaThumbnailSize\MediaThumbnailSizeEntity;
  11. use Shopware\Core\Content\Seo\SeoUrlTemplate\SeoUrlTemplateCollection;
  12. use Shopware\Core\Content\Seo\SeoUrlTemplate\SeoUrlTemplateEntity;
  13. use Shopware\Core\Defaults;
  14. use Shopware\Core\Framework\Context;
  15. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
  16. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  17. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
  18. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  19. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\NotFilter;
  20. use Shopware\Core\Framework\Plugin;
  21. use Shopware\Core\Framework\Plugin\Context\InstallContext;
  22. use Shopware\Core\Framework\Plugin\Context\UninstallContext;
  23. use Shopware\Core\Framework\Plugin\Context\UpdateContext;
  24. use Shopware\Core\Framework\Uuid\Uuid;
  25. use Shopware\Core\Kernel;
  26. use Shopware\Core\System\SystemConfig\SystemConfigService;
  27. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  28. class SasBlogModule extends Plugin
  29. {
  30.     public const ANONYMOUS_AUTHOR_ID '64f4c60194634128b9b85d9299797c45';
  31.     public function install(InstallContext $installContext): void
  32.     {
  33.         parent::install($installContext);
  34.         $this->createBlogMediaFolder($installContext->getContext());
  35.         $this->getLifeCycle()->install($installContext->getContext());
  36.     }
  37.     public function uninstall(UninstallContext $context): void
  38.     {
  39.         parent::uninstall($context);
  40.         // Always remove the SEO url template to avoid error that can't be changed afterward.
  41.         $this->deleteSeoUrlTemplate($context->getContext());
  42.         if ($context->keepUserData()) {
  43.             return;
  44.         }
  45.         /*
  46.          * We need to uninstall our default media folder,
  47.          * the media folder and the thumbnail sizes.
  48.          * However, we have to clean this up within a next update :)
  49.          */
  50.         $this->deleteMediaFolder($context->getContext());
  51.         $this->deleteDefaultMediaFolder($context->getContext());
  52.         /**
  53.          * And of course we need to drop our tables
  54.          */
  55.         $connection Kernel::getConnection();
  56.         $connection->executeStatement('SET FOREIGN_KEY_CHECKS=0;');
  57.         $connection->executeStatement('DROP TABLE IF EXISTS `sas_blog_entries`');
  58.         $connection->executeStatement('DROP TABLE IF EXISTS `sas_blog_entries_translation`');
  59.         $connection->executeStatement('DROP TABLE IF EXISTS `sas_blog_blog_category`');
  60.         $connection->executeStatement('DROP TABLE IF EXISTS `sas_blog_product`');
  61.         $connection->executeStatement('DROP TABLE IF EXISTS `sas_blog_category_translation`');
  62.         $connection->executeStatement('DROP TABLE IF EXISTS `sas_blog_category`');
  63.         $connection->executeStatement('DROP TABLE IF EXISTS `sas_blog_author_translation`');
  64.         $connection->executeStatement('DROP TABLE IF EXISTS `sas_blog_author`');
  65.         /** @var EntityRepository $cmsBlockRepo */
  66.         $cmsBlockRepo $this->container->get('cms_block.repository');
  67.         $context Context::createDefaultContext();
  68.         $criteria = new Criteria();
  69.         $criteria->addFilter(new EqualsAnyFilter('type', ['blog-detail''blog-listing''blog-assignment']));
  70.         $cmsBlocks $cmsBlockRepo->searchIds($criteria$context);
  71.         $cmsBlockRepo->delete(array_values($cmsBlocks->getData()), $context);
  72.         $connection->executeQuery('SET FOREIGN_KEY_CHECKS=1;');
  73.     }
  74.     public function update(UpdateContext $updateContext): void
  75.     {
  76.         parent::update($updateContext);
  77.         (new Update())->update($this->container$updateContext);
  78.         if (version_compare($updateContext->getCurrentPluginVersion(), '1.1.0''<')) {
  79.             $this->createBlogMediaFolder($updateContext->getContext());
  80.         }
  81.         if (version_compare($updateContext->getCurrentPluginVersion(), '1.5.10''<')) {
  82.             $this->fixSeoUrlTemplate($updateContext->getContext());
  83.             $this->updateSeoUrls($updateContext->getContext());
  84.         }
  85.     }
  86.     /**
  87.      * We need to create a folder for the blog media with it's,
  88.      * own configuration to generate thumbnails for the teaser image.
  89.      */
  90.     public function createBlogMediaFolder(Context $context): void
  91.     {
  92.         $this->deleteDefaultMediaFolder($context);
  93.         $thumbnailSizes $this->getThumbnailSizes($context);
  94.         /** @var EntityRepository $mediaFolderRepository */
  95.         $mediaFolderRepository $this->container->get('media_default_folder.repository');
  96.         $data = [
  97.             [
  98.                 'entity' => BlogEntriesDefinition::ENTITY_NAME,
  99.                 'associationFields' => ['media'],
  100.                 'folder' => [
  101.                     'name' => 'Blog Images',
  102.                     'useParentConfiguration' => false,
  103.                     'configuration' => [
  104.                         'createThumbnails' => true,
  105.                         'keepAspectRatio' => true,
  106.                         'thumbnailQuality' => 90,
  107.                         'mediaThumbnailSizes' => $thumbnailSizes,
  108.                     ],
  109.                 ],
  110.             ],
  111.         ];
  112.         $mediaFolderRepository->create($data$context);
  113.     }
  114.     private function deleteDefaultMediaFolder(Context $context): void
  115.     {
  116.         $criteria = new Criteria();
  117.         $criteria->addFilter(
  118.             new EqualsAnyFilter('entity', [
  119.                 BlogEntriesDefinition::ENTITY_NAME,
  120.             ])
  121.         );
  122.         /** @var EntityRepository $mediaFolderRepository */
  123.         $mediaFolderRepository $this->container->get('media_default_folder.repository');
  124.         $mediaFolderIds $mediaFolderRepository->searchIds($criteria$context)->getIds();
  125.         if (!empty($mediaFolderIds)) {
  126.             $ids array_map(static function ($id) {
  127.                 return ['id' => $id];
  128.             }, $mediaFolderIds);
  129.             $mediaFolderRepository->delete($ids$context);
  130.         }
  131.     }
  132.     private function deleteMediaFolder(Context $context): void
  133.     {
  134.         $criteria = new Criteria();
  135.         $criteria->addFilter(
  136.             new EqualsFilter('name''Blog Images')
  137.         );
  138.         /** @var EntityRepository $mediaFolderRepository */
  139.         $mediaFolderRepository $this->container->get('media_folder.repository');
  140.         $mediaFolderRepository->search($criteria$context);
  141.         $mediaFolderIds $mediaFolderRepository->searchIds($criteria$context)->getIds();
  142.         if (!empty($mediaFolderIds)) {
  143.             $ids array_map(static function ($id) {
  144.                 return ['id' => $id];
  145.             }, $mediaFolderIds);
  146.             $mediaFolderRepository->delete($ids$context);
  147.         }
  148.     }
  149.     private function deleteSeoUrlTemplate(Context $context): void
  150.     {
  151.         $criteria = new Criteria();
  152.         $criteria->addFilter(
  153.             new EqualsFilter('entityName''sas_blog_entries')
  154.         );
  155.         /** @var EntityRepository $seoUrlTemplateRepository */
  156.         $seoUrlTemplateRepository $this->container->get('seo_url_template.repository');
  157.         $seoUrlTemplateRepository->search($criteria$context);
  158.         $seoUrlTemplateIds $seoUrlTemplateRepository->searchIds($criteria$context)->getIds();
  159.         if (!empty($seoUrlTemplateIds)) {
  160.             $ids array_map(static function ($id) {
  161.                 return ['id' => $id];
  162.             }, $seoUrlTemplateIds);
  163.             $seoUrlTemplateRepository->delete($ids$context);
  164.         }
  165.     }
  166.     private function getThumbnailSizes(Context $context): array
  167.     {
  168.         $mediaThumbnailSizes = [
  169.             '330x185' => [
  170.                 'width' => 330,
  171.                 'height' => 185,
  172.             ],
  173.             '650x365' => [
  174.                 'width' => 650,
  175.                 'height' => 365,
  176.             ],
  177.             '900x506' => [
  178.                 'width' => 900,
  179.                 'height' => 506,
  180.             ],
  181.             '1280x720' => [
  182.                 'width' => 1280,
  183.                 'height' => 720,
  184.             ],
  185.         ];
  186.         $criteria = new Criteria();
  187.         /** @var EntityRepository $thumbnailSizeRepository */
  188.         $thumbnailSizeRepository $this->container->get('media_thumbnail_size.repository');
  189.         $thumbnailSizes $thumbnailSizeRepository->search($criteria$context)->getEntities();
  190.         $mediaThumbnailSizesAddedIds = [];
  191.         /** @var MediaThumbnailSizeEntity $thumbnailSize */
  192.         foreach ($thumbnailSizes as $thumbnailSize) {
  193.             $key $thumbnailSize->getWidth() . 'x' $thumbnailSize->getHeight();
  194.             if (\array_key_exists($key$mediaThumbnailSizes)) {
  195.                 $mediaThumbnailSize $mediaThumbnailSizes[$key];
  196.                 $mediaThumbnailSizesAddedIds[$key] = array_merge(
  197.                     ['id' => $thumbnailSize->getId()],
  198.                     $mediaThumbnailSize,
  199.                 );
  200.                 unset($mediaThumbnailSizes[$key]);
  201.             }
  202.         }
  203.         $mediaThumbnailSizesCreateData = [];
  204.         foreach ($mediaThumbnailSizes as $key => $mediaThumbnailSize) {
  205.             $data array_merge(
  206.                 ['id' => Uuid::randomHex()],
  207.                 $mediaThumbnailSize,
  208.             );
  209.             $mediaThumbnailSizesCreateData[$key] = $data;
  210.             $mediaThumbnailSizesAddedIds[$key] = $data;
  211.         }
  212.         if (\count($mediaThumbnailSizesCreateData) > 0) {
  213.             $thumbnailSizeRepository->create(array_values($mediaThumbnailSizesCreateData), $context);
  214.         }
  215.         return array_values($mediaThumbnailSizesAddedIds);
  216.     }
  217.     private function getLifeCycle(): Lifecycle
  218.     {
  219.         /** @var SystemConfigService $systemConfig */
  220.         $systemConfig $this->container->get(SystemConfigService::class);
  221.         /** @var EntityRepository $cmsPageRepository */
  222.         $cmsPageRepository $this->container->get('cms_page.repository');
  223.         return new Lifecycle(
  224.             $systemConfig,
  225.             $cmsPageRepository
  226.         );
  227.     }
  228.     private function fixSeoUrlTemplate(Context $context): void
  229.     {
  230.         $criteria = new Criteria();
  231.         $criteria->addFilter(new EqualsFilter('routeName'BlogSeoUrlRoute::ROUTE_NAME));
  232.         $criteria->addFilter(new EqualsFilter('entityName'BlogEntriesDefinition::ENTITY_NAME));
  233.         $criteria->addFilter(new NotFilter(
  234.             NotFilter::CONNECTION_AND,
  235.             [new EqualsFilter('template'null)]
  236.         ));
  237.         /** @var EntityRepository $seoUrlTemplateRepository */
  238.         $seoUrlTemplateRepository $this->container->get('seo_url_template.repository');
  239.         /** @var SeoUrlTemplateCollection $seoUrlTemplates */
  240.         $seoUrlTemplates $seoUrlTemplateRepository->search($criteria$context)->getEntities();
  241.         $update = [];
  242.         /** @var SeoUrlTemplateEntity $seoUrlTemplate */
  243.         foreach ($seoUrlTemplates as $seoUrlTemplate) {
  244.             // If the clients fixed it in SEO template we don't have to do again
  245.             if (strpos($seoUrlTemplate->getTemplate(), 'entry.translated')) {
  246.                 continue;
  247.             }
  248.             // We found the issue
  249.             if (!strpos($seoUrlTemplate->getTemplate(), 'entry.title')) {
  250.                 continue;
  251.             }
  252.             $templateReplaced str_replace('entry.title''entry.translated.title'$seoUrlTemplate->getTemplate());
  253.             if (!\is_string($templateReplaced)) {
  254.                 continue;
  255.             }
  256.             $update[] = [
  257.                 'id' => $seoUrlTemplate->getId(),
  258.                 'template' => $templateReplaced,
  259.             ];
  260.         }
  261.         if (\count($update) === 0) {
  262.             return;
  263.         }
  264.         $seoUrlTemplateRepository->update($update$context);
  265.     }
  266.     private function updateSeoUrls(Context $context): void
  267.     {
  268.         $blogArticlesIds $this->getBlogArticlesIds();
  269.         if (\count($blogArticlesIds) === 0) {
  270.             return;
  271.         }
  272.         if ($this->container->get('event_dispatcher') instanceof EventDispatcherInterface) {
  273.             $eventDispatcher $this->container->get('event_dispatcher');
  274.             $eventDispatcher->dispatch(new BlogIndexerEvent($blogArticlesIds$context));
  275.         }
  276.     }
  277.     private function getBlogArticlesIds(): array
  278.     {
  279.         /** @var Connection $connection */
  280.         $connection $this->container->get(Connection::class);
  281.         if (!$connection->getSchemaManager()->tablesExist([BlogEntriesDefinition::ENTITY_NAME])) {
  282.             return [];
  283.         }
  284.         $now = (new \DateTime())->format(Defaults::STORAGE_DATE_TIME_FORMAT);
  285.         $query $connection->createQueryBuilder();
  286.         $query->select([
  287.             'LOWER(HEX(id)) as id',
  288.         ]);
  289.         $query->where('active = true')->andWhere('published_at <= :now');
  290.         $query->setParameter('now'$now);
  291.         $query->from(BlogEntriesDefinition::ENTITY_NAME);
  292.         if (!$query->execute() instanceof Result) {
  293.             return [];
  294.         }
  295.         $results $query->execute()->fetchAllAssociative();
  296.         if (empty($results)) {
  297.             return [];
  298.         }
  299.         return array_column($results'id');
  300.     }
  301. }