custom/plugins/NewsletterSendinblue/src/NewsletterSendinblue.php line 29

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace NewsletterSendinblue;
  3. use Doctrine\DBAL\Connection;
  4. use Exception;
  5. use Monolog\Logger;
  6. use NewsletterSendinblue\Core\Content\BackInStockSubscription\BackInStockSubscriptionDefinition;
  7. use NewsletterSendinblue\Core\Content\Cart\AbandonedCartDefinition;
  8. use NewsletterSendinblue\Service\ApiClientService;
  9. use NewsletterSendinblue\Service\ConfigService;
  10. use NewsletterSendinblue\Service\IntegrationService;
  11. use NewsletterSendinblue\Service\VersionProvider;
  12. use Shopware\Core\Framework\Context;
  13. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  14. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  15. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  16. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\ContainsFilter;
  17. use Shopware\Core\Framework\Plugin;
  18. use Shopware\Core\Framework\Plugin\Context\DeactivateContext;
  19. use Shopware\Core\Framework\Plugin\Context\InstallContext;
  20. use Shopware\Core\Framework\Plugin\Context\UninstallContext;
  21. use Shopware\Core\Framework\Plugin\Context\UpdateContext;
  22. use Shopware\Core\Framework\Uuid\Uuid;
  23. use Shopware\Core\System\SystemConfig\SystemConfigEntity;
  24. use Shopware\Core\System\SystemConfig\SystemConfigService;
  25. use Symfony\Component\DependencyInjection\ContainerBuilder;
  26. class NewsletterSendinblue extends Plugin
  27. {
  28.     public const ACL_ROLE_NAME 'Brevo';
  29.     public const OLD_ACL_ROLE_NAME 'Sendinblue';
  30.     public const INTEGRATION_LABEL 'Brevo';
  31.     public const OLD_INTEGRATION_LABEL 'Sendinblue';
  32.     public const PLUGIN_LABEL 'Sendinblue';
  33.     public const USER_CONNECTION_ID_CUSTOM_FIELD 'newsletter_sendinblue_user_connection_id';
  34.     public const IGNORE_EVENT 'brevoIgnoreEvent';
  35.     public const BREVO_SMTP_HOST 'smtp-relay.brevo.com';
  36.     public const CUSTOM_TABLES = [
  37.         AbandonedCartDefinition::ENTITY_NAME,
  38.         BackInStockSubscriptionDefinition::ENTITY_NAME
  39.     ];
  40.     /**
  41.      * @var IntegrationService
  42.      */
  43.     private $integrationService;
  44.     /**
  45.      * @param ContainerBuilder $container
  46.      */
  47.     public function build(ContainerBuilder $container): void
  48.     {
  49.         parent::build($container);
  50.         $container->setParameter('sendinblue.service_worker_path'$this->getPath() . '/Resources/js/service-worker.js');
  51.     }
  52.     /**
  53.      * @param InstallContext $installContext
  54.      */
  55.     public function postInstall(InstallContext $installContext): void
  56.     {
  57.         $this->createAclRole($installContext->getContext());
  58.         $this->createIntegrations($installContext->getContext());
  59.     }
  60.     /**
  61.      * @param UpdateContext $updateContext
  62.      * @return void
  63.      */
  64.     public function update(UpdateContext $updateContext): void
  65.     {
  66.         if (version_compare($updateContext->getCurrentPluginVersion(), '3.0.11''<=')) {
  67.             try {
  68.                 $this->changeIntegrationAndRoleName($updateContext->getContext());
  69.             } catch (\Throwable $exception) {
  70.             }
  71.         }
  72.         if (version_compare($updateContext->getCurrentPluginVersion(), '3.1.12''<=')) {
  73.             try {
  74.                 $this->addProductUpdatePrivilegeToAclRole($updateContext->getContext());
  75.             } catch (\Throwable $exception) {
  76.             }
  77.         }
  78.     }
  79.     /**
  80.      * @param UpdateContext $updateContext
  81.      * @return void
  82.      */
  83.     public function postUpdate(UpdateContext $updateContext): void
  84.     {
  85.         $apiClientService $this->getApiClientService($updateContext->getCurrentShopwareVersion());
  86.         try {
  87.             $userConnectionIds $this->getAllUserConnectionIds($updateContext->getContext());
  88.             $data = [
  89.                 'plugin_version' => $updateContext->getUpdatePluginVersion(),
  90.                 'shop_version' => $updateContext->getCurrentShopwareVersion()
  91.             ];
  92.             foreach ($userConnectionIds as $userConnectionId) {
  93.                 $apiClientService->pluginUpdated($data$userConnectionId$updateContext->getContext());
  94.             }
  95.         } catch (\Throwable $e) {
  96.             $apiClientService->getLogger()->addRecord(Logger::ERROR$e->getMessage());
  97.         }
  98.     }
  99.     /**
  100.      * @param DeactivateContext $deactivateContext
  101.      */
  102.     public function deactivate(DeactivateContext $deactivateContext): void
  103.     {
  104.         $this->removeSIBSmtpSettings($deactivateContext->getContext());
  105.     }
  106.     /**
  107.      * @param UninstallContext $uninstallContext
  108.      */
  109.     public function uninstall(UninstallContext $uninstallContext): void
  110.     {
  111.         if (!$uninstallContext->keepUserData()) {
  112.             $this->deleteIntegrations($uninstallContext->getContext());
  113.             $this->deleteAclRole($uninstallContext->getContext());
  114.             $this->deleteAllSendinblueConfigs($uninstallContext->getContext());
  115.             $this->deleteTables();
  116.         }
  117.     }
  118.     /**
  119.      * @param Context $context
  120.      * @return string
  121.      */
  122.     private function createAclRole(Context $context)
  123.     {
  124.         $id md5(self::ACL_ROLE_NAME);
  125.         $roleData = [
  126.             'id' => $id,
  127.             'name' => self::ACL_ROLE_NAME,
  128.             'privileges' => ["customer.editor""customer.viewer""customer:read""customer:update""newsletter_recipient.creator""newsletter_recipient.deleter""newsletter_recipient.editor""newsletter_recipient.viewer""newsletter_recipient:create""newsletter_recipient:delete""newsletter_recipient:read""newsletter_recipient:update""product:update"]
  129.         ];
  130.         $aclRoleRepository $this->container->get('acl_role.repository');
  131.         $context->scope(Context::SYSTEM_SCOPE, function (Context $context) use ($aclRoleRepository$roleData): void {
  132.             $aclRoleRepository->upsert([$roleData], $context);
  133.         });
  134.         return $id;
  135.     }
  136.     /**
  137.      * @param Context $context
  138.      * @return void
  139.      */
  140.     private function addProductUpdatePrivilegeToAclRole(Context $context): void
  141.     {
  142.         $roleId md5(self::ACL_ROLE_NAME); // same ID logic as createAclRole
  143.         $aclRoleRepository $this->container->get('acl_role.repository');
  144.         // Load existing role
  145.         $criteria = new Criteria([$roleId]);
  146.         $role $aclRoleRepository->search($criteria$context)->first();
  147.         if (!$role) {
  148.             return;
  149.         }
  150.         $existingPrivileges $role->getPrivileges();
  151.         $newPrivilege 'product:update';
  152.         // Avoid duplicates
  153.         if (!in_array($newPrivilege$existingPrivilegestrue)) {
  154.             $existingPrivileges[] = $newPrivilege;
  155.             $context->scope(Context::SYSTEM_SCOPE, function (Context $context) use ($roleId$aclRoleRepository$existingPrivileges): void {
  156.                 $aclRoleRepository->update([
  157.                     [
  158.                         'id' => $roleId,
  159.                         'privileges' => $existingPrivileges,
  160.                     ]
  161.                 ], $context);
  162.             });
  163.         }
  164.     }
  165.     /**
  166.      * @param Context $context
  167.      */
  168.     private function createIntegrations(Context $context): void
  169.     {
  170.         $this->getIntegrationService()->createIntegration(self::INTEGRATION_LABEL$context);
  171.     }
  172.     /**
  173.      * @param Context $context
  174.      */
  175.     private function deleteIntegrations(Context $context): void
  176.     {
  177.         $this->getIntegrationService()->deleteIntegrations($context);
  178.     }
  179.     /**
  180.      * @param Context $context
  181.      */
  182.     private function deleteAclRole(Context $context)
  183.     {
  184.         $aclRoleRepository $this->container->get('acl_role.repository');
  185.         $context->scope(Context::SYSTEM_SCOPE, function (Context $context) use ($aclRoleRepository): void {
  186.             $aclRoleRepository->delete([['id' => md5(self::ACL_ROLE_NAME)]], $context);
  187.             $aclRoleRepository->delete([['id' => md5(self::OLD_ACL_ROLE_NAME)]], $context);
  188.         });
  189.     }
  190.     private function deleteAllSendinblueConfigs(Context $context): void
  191.     {
  192.         /** @var EntityRepositoryInterface $systemConfigRepository */
  193.         $systemConfigRepository $this->container->get('system_config.repository');
  194.         $this->removeSIBSmtpSettings($context);
  195.         $criteria = new Criteria();
  196.         $criteria->addFilter(new ContainsFilter('configurationKey'ConfigService::CONFIG_PREFIX));
  197.         $systemConfigIds $systemConfigRepository->searchIds($criteria$context)->getIds();
  198.         if (empty($systemConfigIds)) {
  199.             return;
  200.         }
  201.         $ids array_map(static function ($id) {
  202.             return ['id' => $id];
  203.         }, $systemConfigIds);
  204.         $systemConfigRepository->delete($ids$context);
  205.     }
  206.     /**
  207.      * @param Context $context
  208.      * @return void
  209.      */
  210.     private function removeSIBSmtpSettings(Context $context): void
  211.     {
  212.         /** @var EntityRepositoryInterface $systemConfigRepository */
  213.         $systemConfigRepository $this->container->get('system_config.repository');
  214.         $criteria = new Criteria();
  215.         $criteria->addFilter(new EqualsFilter('configurationKey'ConfigService::prepareConfigName(ConfigService::CONFIG_IS_SMTP_ENABLED)));
  216.         $systemConfigs $systemConfigRepository->search($criteria$context)->getElements();
  217.         if (empty($systemConfigs)) {
  218.             return;
  219.         }
  220.         /** @var SystemConfigService $systemConfigService */
  221.         $systemConfigService $this->container->get(SystemConfigService::class);
  222.         $smtpConfigs = [
  223.             ConfigService::CORE_MAILER_AGENT_CONFIG,
  224.             ConfigService::CORE_MAILER_HOST_CONFIG,
  225.             ConfigService::CORE_MAILER_PORT_CONFIG,
  226.             ConfigService::CORE_MAILER_USERNAME_CONFIG,
  227.             ConfigService::CORE_MAILER_PASSWORD_CONFIG,
  228.             ConfigService::CORE_MAILER_SENDER_CONFIG,
  229.             ConfigService::CORE_MAILER_ENCRYPTION_CONFIG,
  230.             ConfigService::CORE_MAILER_AUTHENTICATION_CONFIG
  231.         ];
  232.         foreach ($systemConfigs as $systemConfig) {
  233.             if ($systemConfig->getConfigurationValue()) {
  234.                 $salesChannelId $systemConfig->getSalesChannelId();
  235.                 $this->removeBrevoSmtpConfigs($systemConfigService$smtpConfigs$salesChannelId);
  236.             }
  237.         }
  238.         // Check global configuration as well
  239.         $this->removeBrevoSmtpConfigs($systemConfigService$smtpConfigs);
  240.     }
  241.     /**
  242.      * @param SystemConfigService $systemConfigService
  243.      * @param array $smtpConfigs
  244.      * @param string|null $salesChannelId
  245.      * @return void
  246.      */
  247.     private function removeBrevoSmtpConfigs(SystemConfigService $systemConfigService, array $smtpConfigs, ?string $salesChannelId null): void
  248.     {
  249.         $currentHost $systemConfigService->get(ConfigService::CORE_MAILER_HOST_CONFIG$salesChannelId);
  250.         if ($currentHost && strpos($currentHostself::BREVO_SMTP_HOST) !== false) {
  251.             foreach ($smtpConfigs as $config) {
  252.                 $systemConfigService->delete($config$salesChannelId);
  253.             }
  254.         }
  255.     }
  256.     /**
  257.      * @return IntegrationService|null
  258.      */
  259.     private function getIntegrationService(): ?IntegrationService
  260.     {
  261.         if (empty($this->integrationService)) {
  262.             if ($this->container->has(IntegrationService::class)) {
  263.                 /** @var IntegrationService integrationService */
  264.                 $this->integrationService $this->container->get(IntegrationService::class);
  265.             } else {
  266.                 /** @var EntityRepositoryInterface $integrationRepository */
  267.                 $integrationRepository $this->container->get('integration.repository');
  268.                 /** @var EntityRepositoryInterface $aclRoleRepository */
  269.                 $aclRoleRepository $this->container->get('acl_role.repository');
  270.                 /** @var SystemConfigService $systemConfigService */
  271.                 $systemConfigService $this->container->get(SystemConfigService::class);
  272.                 $this->integrationService = new IntegrationService($integrationRepository$aclRoleRepository$systemConfigService);
  273.             }
  274.         }
  275.         return $this->integrationService;
  276.     }
  277.     /**
  278.      * @param Context $context
  279.      * @return void
  280.      */
  281.     private function changeIntegrationAndRoleName(Context $context): void
  282.     {
  283.         $connection $this->container->get(Connection::class);
  284.         // change acl_role name
  285.         try {
  286.             $connection->update('acl_role',
  287.                 ['name' => self::ACL_ROLE_NAME],
  288.                 ['name' => self::OLD_ACL_ROLE_NAME]
  289.             );
  290.         } catch (\Throwable $e) {
  291.         }
  292.         // change integration name
  293.         try {
  294.             $integrationRepository $this->container->get('integration.repository');
  295.             $criteria = new Criteria();
  296.             $criteria->addFilter(new EqualsFilter('customFields.sendinblue'1));
  297.             $integrations $integrationRepository->search($criteria$context);
  298.             $data = [];
  299.             foreach ($integrations as $integration) {
  300.                 $data[] = [
  301.                     'id' => $integration->getId(),
  302.                     'label' => str_replace(self::OLD_INTEGRATION_LABELself::INTEGRATION_LABEL$integration->getLabel())
  303.                 ];
  304.             }
  305.             if (!empty($data)) {
  306.                 $integrationRepository->update($data$context);
  307.             }
  308.         } catch (\Throwable $e) {
  309.         }
  310.     }
  311.     /**
  312.      * @param string $currentSwVersion
  313.      * @return ApiClientService
  314.      */
  315.     private function getApiClientService(string $currentSwVersion): ApiClientService
  316.     {
  317.         $configService = new ConfigService(
  318.             $this->container->get('Shopware\Core\System\SystemConfig\SystemConfigService'),
  319.             ApiClientService::SIB_BASE_URL
  320.         );
  321.         $logger = new Logger('Sendinblue');
  322.         $versionProvider = new VersionProvider(
  323.             $currentSwVersion,
  324.             $this->container->get('plugin.repository')
  325.         );
  326.         return new ApiClientService($configService$logger$versionProvider);
  327.     }
  328.     /**
  329.      * @param Context $context
  330.      * @return array
  331.      */
  332.     private function getAllUserConnectionIds(Context $context): array
  333.     {
  334.         $configRepo $this->container->get('system_config.repository');
  335.         $criteria = new Criteria();
  336.         $criteria->addFilter(new EqualsFilter(
  337.                 'configurationKey',
  338.                 ConfigService::CONFIG_PREFIX ConfigService::CONFIG_USER_CONNECTION_ID)
  339.         );
  340.         $sendinblueConfigs $configRepo->search($criteria$context);
  341.         return $sendinblueConfigs->fmap(function (SystemConfigEntity $configEntity) {
  342.             return $configEntity->getConfigurationValue();
  343.         });
  344.     }
  345.     private function deleteTables()
  346.     {
  347.         $connection $this->container->get(Connection::class);
  348.         foreach (self::CUSTOM_TABLES as $table) {
  349.             try {
  350.                 $connection->executeStatement('DROP TABLE IF EXISTS `' $table'`;');
  351.             } catch (Exception $exception) {
  352.             }
  353.         }
  354.     }
  355. }