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

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