Passed
Push — trunk ( 87d13d...17ce15 )
by Christian
14:38 queued 13s
created

AdministrationController::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 20
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 14
dl 0
loc 20
rs 10
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php declare(strict_types=1);
2
3
namespace Shopware\Administration\Controller;
4
5
use Doctrine\DBAL\Connection;
6
use Shopware\Administration\Events\PreResetExcludedSearchTermEvent;
7
use Shopware\Administration\Framework\Routing\KnownIps\KnownIpsCollectorInterface;
8
use Shopware\Administration\Snippet\SnippetFinderInterface;
9
use Shopware\Core\Checkout\Customer\CustomerEntity;
0 ignored issues
show
Bug introduced by
The type Shopware\Core\Checkout\Customer\CustomerEntity was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
10
use Shopware\Core\Defaults;
11
use Shopware\Core\DevOps\Environment\EnvironmentHelper;
12
use Shopware\Core\Framework\Adapter\Twig\TemplateFinder;
13
use Shopware\Core\Framework\Context;
14
use Shopware\Core\Framework\DataAbstractionLayer\DefinitionInstanceRegistry;
15
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
16
use Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\AllowHtml;
17
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
18
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
19
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\MultiFilter;
20
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\NotFilter;
21
use Shopware\Core\Framework\Feature;
22
use Shopware\Core\Framework\Log\Package;
23
use Shopware\Core\Framework\Routing\Exception\InvalidRequestParameterException;
24
use Shopware\Core\Framework\Routing\Exception\LanguageNotFoundException;
25
use Shopware\Core\Framework\Store\Services\FirstRunWizardService;
26
use Shopware\Core\Framework\Util\HtmlSanitizer;
27
use Shopware\Core\Framework\Uuid\Uuid;
28
use Shopware\Core\Framework\Validation\Exception\ConstraintViolationException;
29
use Shopware\Core\PlatformRequest;
30
use Shopware\Core\System\Currency\CurrencyEntity;
0 ignored issues
show
Bug introduced by
The type Shopware\Core\System\Currency\CurrencyEntity was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
31
use Shopware\Core\System\SystemConfig\SystemConfigService;
32
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
33
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
34
use Symfony\Component\HttpFoundation\JsonResponse;
35
use Symfony\Component\HttpFoundation\Request;
36
use Symfony\Component\HttpFoundation\Response;
37
use Symfony\Component\Routing\Annotation\Route;
38
use Symfony\Component\Validator\ConstraintViolation;
39
use Symfony\Component\Validator\ConstraintViolationList;
40
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
41
use function version_compare;
42
43
#[Route(defaults: ['_routeScope' => ['administration']])]
44
#[Package('administration')]
45
class AdministrationController extends AbstractController
46
{
47
    private readonly bool $esAdministrationEnabled;
48
49
    /**
50
     * @internal
51
     *
52
     * @param array<int, int> $supportedApiVersions
53
     */
54
    public function __construct(
55
        private readonly TemplateFinder $finder,
56
        private readonly FirstRunWizardService $firstRunWizardService,
57
        private readonly SnippetFinderInterface $snippetFinder,
58
        private readonly array $supportedApiVersions,
59
        private readonly KnownIpsCollectorInterface $knownIpsCollector,
60
        private readonly Connection $connection,
61
        private readonly EventDispatcherInterface $eventDispatcher,
62
        private readonly string $shopwareCoreDir,
63
        private readonly EntityRepository $customerRepo,
64
        private readonly EntityRepository $currencyRepository,
65
        private readonly HtmlSanitizer $htmlSanitizer,
66
        private readonly DefinitionInstanceRegistry $definitionInstanceRegistry,
67
        ParameterBagInterface $params,
68
        private readonly SystemConfigService $systemConfigService
69
    ) {
70
        // param is only available if the elasticsearch bundle is enabled
71
        $this->esAdministrationEnabled = $params->has('elasticsearch.administration.enabled')
0 ignored issues
show
Bug introduced by
The property esAdministrationEnabled is declared read-only in Shopware\Administration\...dministrationController.
Loading history...
72
            ? $params->get('elasticsearch.administration.enabled')
73
            : false;
74
    }
75
76
    #[Route(path: '/%shopware_administration.path_name%', name: 'administration.index', defaults: ['auth_required' => false], methods: ['GET'])]
77
    public function index(Request $request, Context $context): Response
78
    {
79
        $template = $this->finder->find('@Administration/administration/index.html.twig');
80
81
        /** @var CurrencyEntity $defaultCurrency */
82
        $defaultCurrency = $this->currencyRepository->search(new Criteria([Defaults::CURRENCY]), $context)->first();
83
84
        return $this->render($template, [
85
            'features' => Feature::getAll(),
86
            'systemLanguageId' => Defaults::LANGUAGE_SYSTEM,
87
            'defaultLanguageIds' => [Defaults::LANGUAGE_SYSTEM],
88
            'systemCurrencyId' => Defaults::CURRENCY,
89
            'disableExtensions' => EnvironmentHelper::getVariable('DISABLE_EXTENSIONS', false),
90
            'systemCurrencyISOCode' => $defaultCurrency->getIsoCode(),
91
            'liveVersionId' => Defaults::LIVE_VERSION,
92
            'firstRunWizard' => $this->firstRunWizardService->frwShouldRun(),
93
            'apiVersion' => $this->getLatestApiVersion(),
94
            'cspNonce' => $request->attributes->get(PlatformRequest::ATTRIBUTE_CSP_NONCE),
95
            'adminEsEnable' => $this->esAdministrationEnabled,
96
        ]);
97
    }
98
99
    #[Route(path: '/api/_admin/snippets', name: 'api.admin.snippets', methods: ['GET'])]
100
    public function snippets(Request $request): Response
101
    {
102
        $snippets = [];
103
        $locale = $request->query->get('locale', 'en-GB');
104
        $snippets[$locale] = $this->snippetFinder->findSnippets((string) $locale);
105
106
        if ($locale !== 'en-GB') {
107
            $snippets['en-GB'] = $this->snippetFinder->findSnippets('en-GB');
108
        }
109
110
        return new JsonResponse($snippets);
111
    }
112
113
    #[Route(path: '/api/_admin/known-ips', name: 'api.admin.known-ips', methods: ['GET'])]
114
    public function knownIps(Request $request): Response
115
    {
116
        $ips = [];
117
118
        foreach ($this->knownIpsCollector->collectIps($request) as $ip => $name) {
119
            $ips[] = [
120
                'name' => $name,
121
                'value' => $ip,
122
            ];
123
        }
124
125
        return new JsonResponse(['ips' => $ips]);
126
    }
127
128
    #[Route(path: '/api/_admin/reset-excluded-search-term', name: 'api.admin.reset-excluded-search-term', defaults: ['_acl' => ['system_config:update', 'system_config:create', 'system_config:delete']], methods: ['POST'])]
129
    public function resetExcludedSearchTerm(Context $context): JsonResponse
130
    {
131
        $searchConfigId = $this->connection->fetchOne('SELECT id FROM product_search_config WHERE language_id = :language_id', ['language_id' => Uuid::fromHexToBytes($context->getLanguageId())]);
132
133
        if ($searchConfigId === false) {
134
            throw new LanguageNotFoundException($context->getLanguageId());
135
        }
136
137
        $deLanguageId = $this->fetchLanguageIdByName('de-DE', $this->connection);
138
        $enLanguageId = $this->fetchLanguageIdByName('en-GB', $this->connection);
139
140
        switch ($context->getLanguageId()) {
141
            case $deLanguageId:
142
                $defaultExcludedTerm = require $this->shopwareCoreDir . '/Migration/Fixtures/stopwords/de.php';
143
144
                break;
145
            case $enLanguageId:
146
                $defaultExcludedTerm = require $this->shopwareCoreDir . '/Migration/Fixtures/stopwords/en.php';
147
148
                break;
149
            default:
150
                /** @var PreResetExcludedSearchTermEvent $preResetExcludedSearchTermEvent */
151
                $preResetExcludedSearchTermEvent = $this->eventDispatcher->dispatch(new PreResetExcludedSearchTermEvent($searchConfigId, [], $context));
152
                $defaultExcludedTerm = $preResetExcludedSearchTermEvent->getExcludedTerms();
153
        }
154
155
        $this->connection->executeStatement(
156
            'UPDATE `product_search_config` SET `excluded_terms` = :excludedTerms WHERE `id` = :id',
157
            [
158
                'excludedTerms' => json_encode($defaultExcludedTerm, \JSON_THROW_ON_ERROR),
159
                'id' => $searchConfigId,
160
            ]
161
        );
162
163
        return new JsonResponse([
164
            'success' => true,
165
        ]);
166
    }
167
168
    #[Route(path: '/api/_admin/check-customer-email-valid', name: 'api.admin.check-customer-email-valid', methods: ['POST'])]
169
    public function checkCustomerEmailValid(Request $request, Context $context): JsonResponse
170
    {
171
        $params = [];
172
        if (!$request->request->has('email')) {
173
            throw new \InvalidArgumentException('Parameter "email" is missing.');
174
        }
175
176
        $email = (string) $request->request->get('email');
177
        $isCustomerBoundSalesChannel = $this->systemConfigService->get('core.systemWideLoginRegistration.isCustomerBoundToSalesChannel');
178
        $boundSalesChannelId = null;
179
        if ($isCustomerBoundSalesChannel) {
180
            $boundSalesChannelId = $request->request->get('boundSalesChannelId');
181
            if ($boundSalesChannelId !== null && !\is_string($boundSalesChannelId)) {
182
                throw new InvalidRequestParameterException('boundSalesChannelId');
183
            }
184
        }
185
186
        $customer = $this->getCustomerByEmail((string) $request->request->get('id'), $email, $context, $boundSalesChannelId);
187
        if (!$customer) {
188
            return new JsonResponse(
189
                ['isValid' => true]
190
            );
191
        }
192
193
        $message = 'The email address {{ email }} is already in use';
194
        $params['{{ email }}'] = $email;
195
196
        if ($customer->getBoundSalesChannel()) {
197
            $message .= ' in the Sales Channel {{ salesChannel }}';
198
            $params['{{ salesChannel }}'] = $customer->getBoundSalesChannel()->getName();
199
        }
200
201
        $violations = new ConstraintViolationList();
202
        $violations->add(new ConstraintViolation(
203
            str_replace(array_keys($params), array_values($params), $message),
204
            $message,
205
            $params,
206
            null,
207
            null,
208
            $email,
209
            null,
210
            '79d30fe0-febf-421e-ac9b-1bfd5c9007f7'
211
        ));
212
213
        throw new ConstraintViolationException($violations, $request->request->all());
214
    }
215
216
    #[Route(path: '/api/_admin/sanitize-html', name: 'api.admin.sanitize-html', methods: ['POST'])]
217
    public function sanitizeHtml(Request $request, Context $context): JsonResponse
0 ignored issues
show
Unused Code introduced by
The parameter $context is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

217
    public function sanitizeHtml(Request $request, /** @scrutinizer ignore-unused */ Context $context): JsonResponse

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
218
    {
219
        if (!$request->request->has('html')) {
220
            throw new \InvalidArgumentException('Parameter "html" is missing.');
221
        }
222
223
        $html = (string) $request->request->get('html');
224
        $field = (string) $request->request->get('field');
225
226
        if ($field === '') {
227
            return new JsonResponse(
228
                ['preview' => $this->htmlSanitizer->sanitize($html)]
229
            );
230
        }
231
232
        [$entityName, $propertyName] = explode('.', $field);
233
        $property = $this->definitionInstanceRegistry->getByEntityName($entityName)->getField($propertyName);
234
235
        if ($property === null) {
236
            throw new \InvalidArgumentException('Invalid field property provided.');
237
        }
238
239
        $flag = $property->getFlag(AllowHtml::class);
240
241
        if ($flag === null) {
242
            return new JsonResponse(
243
                ['preview' => strip_tags($html)]
244
            );
245
        }
246
247
        if ($flag instanceof AllowHtml && !$flag->isSanitized()) {
248
            return new JsonResponse(
249
                ['preview' => $html]
250
            );
251
        }
252
253
        return new JsonResponse(
254
            ['preview' => $this->htmlSanitizer->sanitize($html, [], false, $field)]
255
        );
256
    }
257
258
    private function fetchLanguageIdByName(string $isoCode, Connection $connection): ?string
259
    {
260
        $languageId = $connection->fetchOne(
261
            '
262
            SELECT `language`.id FROM `language`
263
            INNER JOIN locale ON language.translation_code_id = locale.id
264
            WHERE `code` = :code',
265
            ['code' => $isoCode]
266
        );
267
268
        return $languageId === false ? null : Uuid::fromBytesToHex($languageId);
269
    }
270
271
    private function getLatestApiVersion(): ?int
272
    {
273
        $sortedSupportedApiVersions = array_values($this->supportedApiVersions);
274
275
        usort($sortedSupportedApiVersions, fn (int $version1, int $version2) => version_compare((string) $version1, (string) $version2));
276
277
        return array_pop($sortedSupportedApiVersions);
278
    }
279
280
    private function getCustomerByEmail(string $customerId, string $email, Context $context, ?string $boundSalesChannelId): ?CustomerEntity
281
    {
282
        $criteria = new Criteria();
283
        $criteria->setLimit(1);
284
        if ($boundSalesChannelId) {
285
            $criteria->addAssociation('boundSalesChannel');
286
        }
287
288
        $criteria->addFilter(new EqualsFilter('email', $email));
289
        $criteria->addFilter(new EqualsFilter('guest', false));
290
        $criteria->addFilter(new NotFilter(
291
            NotFilter::CONNECTION_AND,
292
            [new EqualsFilter('id', $customerId)]
293
        ));
294
295
        $criteria->addFilter(new MultiFilter(MultiFilter::CONNECTION_OR, [
296
            new EqualsFilter('boundSalesChannelId', null),
297
            new EqualsFilter('boundSalesChannelId', $boundSalesChannelId),
298
        ]));
299
300
        /** @var ?CustomerEntity $customer */
301
        $customer = $this->customerRepo->search($criteria, $context)->first();
302
303
        return $customer;
304
    }
305
}
306