vendor/shopware/administration/Controller/AdministrationController.php line 101

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Administration\Controller;
  3. use Doctrine\DBAL\Connection;
  4. use Shopware\Administration\Events\PreResetExcludedSearchTermEvent;
  5. use Shopware\Administration\Framework\Routing\KnownIps\KnownIpsCollectorInterface;
  6. use Shopware\Administration\Snippet\SnippetFinderInterface;
  7. use Shopware\Core\Defaults;
  8. use Shopware\Core\DevOps\Environment\EnvironmentHelper;
  9. use Shopware\Core\Framework\Adapter\Twig\TemplateFinder;
  10. use Shopware\Core\Framework\Context;
  11. use Shopware\Core\Framework\DataAbstractionLayer\DefinitionInstanceRegistry;
  12. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  13. use Shopware\Core\Framework\DataAbstractionLayer\Exception\InconsistentCriteriaIdsException;
  14. use Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\AllowHtml;
  15. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  16. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  17. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\MultiFilter;
  18. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\NotFilter;
  19. use Shopware\Core\Framework\Feature;
  20. use Shopware\Core\Framework\Routing\Annotation\Since;
  21. use Shopware\Core\Framework\Routing\Exception\InvalidRequestParameterException;
  22. use Shopware\Core\Framework\Routing\Exception\LanguageNotFoundException;
  23. use Shopware\Core\Framework\Store\Services\FirstRunWizardClient;
  24. use Shopware\Core\Framework\Util\HtmlSanitizer;
  25. use Shopware\Core\Framework\Uuid\Uuid;
  26. use Shopware\Core\Framework\Validation\Exception\ConstraintViolationException;
  27. use Shopware\Core\PlatformRequest;
  28. use Shopware\Core\System\Currency\CurrencyEntity;
  29. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  30. use Symfony\Component\HttpFoundation\JsonResponse;
  31. use Symfony\Component\HttpFoundation\Request;
  32. use Symfony\Component\HttpFoundation\Response;
  33. use Symfony\Component\Routing\Annotation\Route;
  34. use Symfony\Component\Validator\ConstraintViolation;
  35. use Symfony\Component\Validator\ConstraintViolationList;
  36. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  37. /**
  38.  * @Route(defaults={"_routeScope"={"administration"}})
  39.  */
  40. class AdministrationController extends AbstractController
  41. {
  42.     private TemplateFinder $finder;
  43.     private FirstRunWizardClient $firstRunWizardClient;
  44.     private SnippetFinderInterface $snippetFinder;
  45.     private array $supportedApiVersions;
  46.     private KnownIpsCollectorInterface $knownIpsCollector;
  47.     private Connection $connection;
  48.     private EventDispatcherInterface $eventDispatcher;
  49.     private string $shopwareCoreDir;
  50.     private EntityRepositoryInterface $customerRepo;
  51.     private EntityRepositoryInterface $currencyRepository;
  52.     private HtmlSanitizer $htmlSanitizer;
  53.     private DefinitionInstanceRegistry $definitionInstanceRegistry;
  54.     public function __construct(
  55.         TemplateFinder $finder,
  56.         FirstRunWizardClient $firstRunWizardClient,
  57.         SnippetFinderInterface $snippetFinder,
  58.         array $supportedApiVersions,
  59.         KnownIpsCollectorInterface $knownIpsCollector,
  60.         Connection $connection,
  61.         EventDispatcherInterface $eventDispatcher,
  62.         string $shopwareCoreDir,
  63.         EntityRepositoryInterface $customerRepo,
  64.         EntityRepositoryInterface $currencyRepository,
  65.         HtmlSanitizer $htmlSanitizer,
  66.         DefinitionInstanceRegistry $definitionInstanceRegistry
  67.     ) {
  68.         $this->finder $finder;
  69.         $this->firstRunWizardClient $firstRunWizardClient;
  70.         $this->snippetFinder $snippetFinder;
  71.         $this->supportedApiVersions $supportedApiVersions;
  72.         $this->knownIpsCollector $knownIpsCollector;
  73.         $this->connection $connection;
  74.         $this->eventDispatcher $eventDispatcher;
  75.         $this->shopwareCoreDir $shopwareCoreDir;
  76.         $this->customerRepo $customerRepo;
  77.         $this->currencyRepository $currencyRepository;
  78.         $this->htmlSanitizer $htmlSanitizer;
  79.         $this->definitionInstanceRegistry $definitionInstanceRegistry;
  80.     }
  81.     /**
  82.      * @Since("6.3.3.0")
  83.      * @Route("/%shopware_administration.path_name%", defaults={"auth_required"=false}, name="administration.index", methods={"GET"})
  84.      */
  85.     public function index(Request $requestContext $context): Response
  86.     {
  87.         $template $this->finder->find('@Administration/administration/index.html.twig');
  88.         /** @var CurrencyEntity $defaultCurrency */
  89.         $defaultCurrency $this->currencyRepository->search(new Criteria([Defaults::CURRENCY]), $context)->first();
  90.         return $this->render($template, [
  91.             'features' => Feature::getAll(),
  92.             'systemLanguageId' => Defaults::LANGUAGE_SYSTEM,
  93.             'defaultLanguageIds' => [Defaults::LANGUAGE_SYSTEM],
  94.             'systemCurrencyId' => Defaults::CURRENCY,
  95.             'disableExtensions' => EnvironmentHelper::getVariable('DISABLE_EXTENSIONS'false),
  96.             'systemCurrencyISOCode' => $defaultCurrency->getIsoCode(),
  97.             'liveVersionId' => Defaults::LIVE_VERSION,
  98.             'firstRunWizard' => $this->firstRunWizardClient->frwShouldRun(),
  99.             'apiVersion' => $this->getLatestApiVersion(),
  100.             'cspNonce' => $request->attributes->get(PlatformRequest::ATTRIBUTE_CSP_NONCE),
  101.         ]);
  102.     }
  103.     /**
  104.      * @Since("6.1.0.0")
  105.      * @Route("/api/_admin/snippets", name="api.admin.snippets", methods={"GET"})
  106.      */
  107.     public function snippets(Request $request): Response
  108.     {
  109.         $locale $request->query->get('locale''en-GB');
  110.         $snippets[$locale] = $this->snippetFinder->findSnippets((string) $locale);
  111.         if ($locale !== 'en-GB') {
  112.             $snippets['en-GB'] = $this->snippetFinder->findSnippets('en-GB');
  113.         }
  114.         return new JsonResponse($snippets);
  115.     }
  116.     /**
  117.      * @Since("6.3.1.0")
  118.      * @Route("/api/_admin/known-ips", name="api.admin.known-ips", methods={"GET"})
  119.      */
  120.     public function knownIps(Request $request): Response
  121.     {
  122.         $ips = [];
  123.         foreach ($this->knownIpsCollector->collectIps($request) as $ip => $name) {
  124.             $ips[] = [
  125.                 'name' => $name,
  126.                 'value' => $ip,
  127.             ];
  128.         }
  129.         return new JsonResponse(['ips' => $ips]);
  130.     }
  131.     /**
  132.      * @deprecated tag:v6.5.0 - native return type JsonResponse will be added
  133.      *
  134.      * @Since("6.4.0.1")
  135.      * @Route("/api/_admin/reset-excluded-search-term", name="api.admin.reset-excluded-search-term", methods={"POST"}, defaults={"_acl"={"system_config:update", "system_config:create", "system_config:delete"}})
  136.      *
  137.      * @return JsonResponse
  138.      */
  139.     public function resetExcludedSearchTerm(Context $context)
  140.     {
  141.         $searchConfigId $this->connection->fetchColumn('SELECT id FROM product_search_config WHERE language_id = :language_id', ['language_id' => Uuid::fromHexToBytes($context->getLanguageId())]);
  142.         if ($searchConfigId === false) {
  143.             throw new LanguageNotFoundException($context->getLanguageId());
  144.         }
  145.         $deLanguageId $this->fetchLanguageIdByName('de-DE'$this->connection);
  146.         $enLanguageId $this->fetchLanguageIdByName('en-GB'$this->connection);
  147.         switch ($context->getLanguageId()) {
  148.             case $deLanguageId:
  149.                 $defaultExcludedTerm = require $this->shopwareCoreDir '/Migration/Fixtures/stopwords/de.php';
  150.                 break;
  151.             case $enLanguageId:
  152.                 $defaultExcludedTerm = require $this->shopwareCoreDir '/Migration/Fixtures/stopwords/en.php';
  153.                 break;
  154.             default:
  155.                 /** @var PreResetExcludedSearchTermEvent $preResetExcludedSearchTermEvent */
  156.                 $preResetExcludedSearchTermEvent $this->eventDispatcher->dispatch(new PreResetExcludedSearchTermEvent($searchConfigId, [], $context));
  157.                 $defaultExcludedTerm $preResetExcludedSearchTermEvent->getExcludedTerms();
  158.         }
  159.         $this->connection->executeUpdate(
  160.             'UPDATE `product_search_config` SET `excluded_terms` = :excludedTerms WHERE `id` = :id',
  161.             [
  162.                 'excludedTerms' => json_encode($defaultExcludedTerm),
  163.                 'id' => $searchConfigId,
  164.             ]
  165.         );
  166.         return new JsonResponse([
  167.             'success' => true,
  168.         ]);
  169.     }
  170.     /**
  171.      * @Since("6.4.0.1")
  172.      * @Route("/api/_admin/check-customer-email-valid", name="api.admin.check-customer-email-valid", methods={"POST"})
  173.      */
  174.     public function checkCustomerEmailValid(Request $requestContext $context): JsonResponse
  175.     {
  176.         if (!$request->request->has('email')) {
  177.             throw new \InvalidArgumentException('Parameter "email" is missing.');
  178.         }
  179.         $email = (string) $request->request->get('email');
  180.         $boundSalesChannelId $request->request->get('bound_sales_channel_id');
  181.         if ($boundSalesChannelId !== null && !\is_string($boundSalesChannelId)) {
  182.             throw new InvalidRequestParameterException('bound_sales_channel_id');
  183.         }
  184.         if ($this->isEmailValid((string) $request->request->get('id'), $email$context$boundSalesChannelId)) {
  185.             return new JsonResponse(
  186.                 ['isValid' => true]
  187.             );
  188.         }
  189.         $message 'The email address {{ email }} is already in use';
  190.         $params['{{ email }}'] = $email;
  191.         if ($boundSalesChannelId !== null) {
  192.             $message .= ' in the sales channel {{ salesChannelId }}';
  193.             $params['{{ salesChannelId }}'] = $boundSalesChannelId;
  194.         }
  195.         $violations = new ConstraintViolationList();
  196.         $violations->add(new ConstraintViolation(
  197.             str_replace(array_keys($params), array_values($params), $message),
  198.             $message,
  199.             $params,
  200.             null,
  201.             null,
  202.             $email,
  203.             null,
  204.             '79d30fe0-febf-421e-ac9b-1bfd5c9007f7'
  205.         ));
  206.         throw new ConstraintViolationException($violations$request->request->all());
  207.     }
  208.     /**
  209.      * @Since("6.4.2.0")
  210.      * @Route("/api/_admin/sanitize-html", name="api.admin.sanitize-html", methods={"POST"})
  211.      */
  212.     public function sanitizeHtml(Request $requestContext $context): JsonResponse
  213.     {
  214.         if (!$request->request->has('html')) {
  215.             throw new \InvalidArgumentException('Parameter "html" is missing.');
  216.         }
  217.         $html = (string) $request->request->get('html');
  218.         $field = (string) $request->request->get('field');
  219.         if ($field === '') {
  220.             return new JsonResponse(
  221.                 ['preview' => $this->htmlSanitizer->sanitize($html)]
  222.             );
  223.         }
  224.         [$entityName$propertyName] = explode('.'$field);
  225.         $property $this->definitionInstanceRegistry->getByEntityName($entityName)->getField($propertyName);
  226.         if ($property === null) {
  227.             throw new \InvalidArgumentException('Invalid field property provided.');
  228.         }
  229.         $flag $property->getFlag(AllowHtml::class);
  230.         if ($flag === null) {
  231.             return new JsonResponse(
  232.                 ['preview' => strip_tags($html)]
  233.             );
  234.         }
  235.         if ($flag instanceof AllowHtml && !$flag->isSanitized()) {
  236.             return new JsonResponse(
  237.                 ['preview' => $html]
  238.             );
  239.         }
  240.         return new JsonResponse(
  241.             ['preview' => $this->htmlSanitizer->sanitize($html, [], false$field)]
  242.         );
  243.     }
  244.     private function fetchLanguageIdByName(string $isoCodeConnection $connection): ?string
  245.     {
  246.         $languageId $connection->fetchColumn(
  247.             '
  248.             SELECT `language`.id FROM `language`
  249.             INNER JOIN locale ON language.translation_code_id = locale.id
  250.             WHERE `code` = :code',
  251.             ['code' => $isoCode]
  252.         );
  253.         return $languageId === false null Uuid::fromBytesToHex($languageId);
  254.     }
  255.     private function getLatestApiVersion(): int
  256.     {
  257.         $sortedSupportedApiVersions array_values($this->supportedApiVersions);
  258.         usort($sortedSupportedApiVersions'version_compare');
  259.         return array_pop($sortedSupportedApiVersions);
  260.     }
  261.     /**
  262.      * @throws InconsistentCriteriaIdsException
  263.      */
  264.     private function isEmailValid(string $customerIdstring $emailContext $context, ?string $boundSalesChannelId): bool
  265.     {
  266.         $criteria = new Criteria();
  267.         $criteria->addFilter(new EqualsFilter('email'$email));
  268.         $criteria->addFilter(new EqualsFilter('guest'false));
  269.         $criteria->addFilter(new NotFilter(
  270.             NotFilter::CONNECTION_AND,
  271.             [new EqualsFilter('id'$customerId)]
  272.         ));
  273.         $criteria->addFilter(new MultiFilter(MultiFilter::CONNECTION_OR, [
  274.             new EqualsFilter('boundSalesChannelId'null),
  275.             new EqualsFilter('boundSalesChannelId'$boundSalesChannelId),
  276.         ]));
  277.         return $this->customerRepo->searchIds($criteria$context)->getTotal() === 0;
  278.     }
  279. }