Passed
Pull Request — master (#6993)
by
unknown
09:17
created

SettingsController::updateSetting()   C

Complexity

Conditions 11
Paths 55

Size

Total Lines 93
Code Lines 60

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 11
eloc 60
c 1
b 0
f 0
nc 55
nop 3
dl 0
loc 93
rs 6.726

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/* For licensing terms, see /license.txt */
6
7
namespace Chamilo\CoreBundle\Controller\Admin;
8
9
use Chamilo\CoreBundle\Controller\BaseController;
10
use Chamilo\CoreBundle\Entity\SettingsCurrent;
11
use Chamilo\CoreBundle\Entity\SettingsValueTemplate;
12
use Chamilo\CoreBundle\Helpers\AccessUrlHelper;
13
use Chamilo\CoreBundle\Traits\ControllerTrait;
14
use Collator;
15
use Doctrine\ORM\EntityManagerInterface;
16
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
17
use Symfony\Component\Form\Extension\Core\Type\TextType;
18
use Symfony\Component\Form\FormInterface;
19
use Symfony\Component\HttpFoundation\JsonResponse;
20
use Symfony\Component\HttpFoundation\Request;
21
use Symfony\Component\HttpFoundation\Response;
22
use Symfony\Component\Routing\Attribute\Route;
23
use Symfony\Component\Security\Http\Attribute\IsGranted;
24
use Symfony\Component\Validator\Exception\ValidatorException;
25
use Symfony\Contracts\Translation\TranslatorInterface;
26
27
#[Route('/admin')]
28
class SettingsController extends BaseController
29
{
30
    use ControllerTrait;
31
32
    public function __construct(
33
        private readonly EntityManagerInterface $entityManager,
34
        private readonly TranslatorInterface $translator
35
    ) {}
36
37
    #[Route('/settings', name: 'admin_settings')]
38
    public function index(): Response
39
    {
40
        return $this->redirectToRoute('chamilo_platform_settings', ['namespace' => 'platform']);
41
    }
42
43
    /**
44
     * Edit configuration with given namespace.
45
     */
46
    #[IsGranted('ROLE_ADMIN')]
47
    #[Route('/settings/search_settings', name: 'chamilo_platform_settings_search')]
48
    public function searchSetting(Request $request, AccessUrlHelper $accessUrlHelper): Response
49
    {
50
        $manager = $this->getSettingsManager();
51
52
        $url = $accessUrlHelper->getCurrent();
53
        $manager->setUrl($url);
54
55
        $formList = [];
56
        $templateMap = [];
57
        $templateMapByCategory = [];
58
        $settings = [];
59
60
        $keyword = trim((string) $request->query->get('keyword', ''));
61
62
        $searchForm = $this->getSearchForm();
63
        $searchForm->handleRequest($request);
64
        if ($searchForm->isSubmitted() && $searchForm->isValid()) {
65
            $values = $searchForm->getData();
66
            $keyword = trim((string) ($values['keyword'] ?? ''));
67
        }
68
69
        $schemas = $manager->getSchemas();
70
        [$ordered, $labelMap] = $this->computeOrderedNamespacesByTranslatedLabel($schemas, $request);
71
72
        if ('' === $keyword) {
73
            return $this->render('@ChamiloCore/Admin/Settings/search.html.twig', [
74
                'keyword' => $keyword,
75
                'schemas' => $schemas,
76
                'settings' => $settings,
77
                'form_list' => $formList,
78
                'search_form' => $searchForm->createView(),
79
                'template_map' => $templateMap,
80
                'template_map_by_category' => $templateMapByCategory,
81
                'ordered_namespaces' => $ordered,
82
                'namespace_labels' => $labelMap,
83
            ]);
84
        }
85
86
        $settingsRepo = $this->entityManager->getRepository(SettingsCurrent::class);
87
        $settingsWithTemplate = $settingsRepo->findBy(['url' => $url]);
88
        foreach ($settingsWithTemplate as $s) {
89
            if ($s->getValueTemplate()) {
90
                $templateMap[$s->getVariable()] = $s->getValueTemplate()->getId();
91
            }
92
        }
93
94
        $settingsFromKeyword = $manager->getParametersFromKeywordOrderedByCategory($keyword);
95
        if (!empty($settingsFromKeyword)) {
96
            foreach ($settingsFromKeyword as $category => $parameterList) {
97
                if (empty($category)) {
98
                    continue;
99
                }
100
101
                $variablesInCategory = [];
102
                foreach ($parameterList as $parameter) {
103
                    $var = $parameter->getVariable();
104
                    $variablesInCategory[] = $var;
105
                    if (isset($templateMap[$var])) {
106
                        $templateMapByCategory[$category][$var] = $templateMap[$var];
107
                    }
108
                }
109
110
                // Convert category to schema alias and validate it BEFORE loading/creating the form
111
                $schemaAlias = $manager->convertNameSpaceToService($category);
112
113
                // skip unknown/legacy categories (e.g., "tools")
114
                if (!isset($schemas[$schemaAlias])) {
115
                    continue;
116
                }
117
118
                $settings = $manager->load($category);
119
                $form = $this->getSettingsFormFactory()->create($schemaAlias);
120
121
                foreach (array_keys($settings->getParameters()) as $name) {
122
                    if (!\in_array($name, $variablesInCategory, true)) {
123
                        $form->remove($name);
124
                        $settings->remove($name);
125
                    }
126
                }
127
                $form->setData($settings);
128
                $formList[$category] = $form->createView();
129
            }
130
        }
131
132
        return $this->render('@ChamiloCore/Admin/Settings/search.html.twig', [
133
            'keyword' => $keyword,
134
            'schemas' => $schemas,
135
            'settings' => $settings,
136
            'form_list' => $formList,
137
            'search_form' => $searchForm->createView(),
138
            'template_map' => $templateMap,
139
            'template_map_by_category' => $templateMapByCategory,
140
            'ordered_namespaces' => $ordered,
141
            'namespace_labels' => $labelMap,
142
        ]);
143
    }
144
145
    /**
146
     * Edit configuration with given namespace.
147
     */
148
    #[IsGranted('ROLE_ADMIN')]
149
    #[Route('/settings/{namespace}', name: 'chamilo_platform_settings')]
150
    public function updateSetting(Request $request, AccessUrlHelper $accessUrlHelper, string $namespace): Response
151
    {
152
        $manager = $this->getSettingsManager();
153
        $url = $accessUrlHelper->getCurrent();
154
        $manager->setUrl($url);
155
        $schemaAlias = $manager->convertNameSpaceToService($namespace);
156
157
        $keyword = (string) $request->query->get('keyword', '');
158
159
        // Validate schema BEFORE load/create to avoid NonExistingServiceException
160
        $schemas = $manager->getSchemas();
161
        if (!isset($schemas[$schemaAlias])) {
162
            $this->addFlash('warning', sprintf('Unknown settings category "%s". Showing Platform settings.', $namespace));
163
164
            return $this->redirectToRoute('chamilo_platform_settings', [
165
                'namespace' => 'platform',
166
            ]);
167
        }
168
169
        $settings = $manager->load($namespace);
170
171
        $form = $this->getSettingsFormFactory()->create(
172
            $schemaAlias,
173
            null,
174
            ['allow_extra_fields' => true]
175
        );
176
177
        $form->setData($settings);
178
179
        $isPartial =
180
            $request->isMethod('PATCH')
181
            || 'PATCH' === strtoupper((string) $request->request->get('_method'))
182
            || $request->request->getBoolean('_partial', false);
183
184
        if ($isPartial) {
185
            $payload = $request->request->all($form->getName());
186
            $form->submit($payload, false);
187
        } else {
188
            $form->handleRequest($request);
189
        }
190
191
        if ($form->isSubmitted() && $form->isValid()) {
192
            $messageType = 'success';
193
194
            try {
195
                $manager->save($form->getData());
196
                $message = $this->trans('The settings have been stored');
197
            } catch (ValidatorException $validatorException) {
198
                $message = $this->trans($validatorException->getMessage());
199
                $messageType = 'error';
200
            }
201
202
            $this->addFlash($messageType, $message);
203
204
            if ('' !== $keyword) {
205
                return $this->redirectToRoute('chamilo_platform_settings_search', [
206
                    'keyword' => $keyword,
207
                ]);
208
            }
209
210
            return $this->redirectToRoute('chamilo_platform_settings', [
211
                'namespace' => $namespace,
212
            ]);
213
        }
214
215
        [$ordered, $labelMap] = $this->computeOrderedNamespacesByTranslatedLabel($schemas, $request);
216
217
        $templateMap = [];
218
        $settingsRepo = $this->entityManager->getRepository(SettingsCurrent::class);
219
220
        $settingsWithTemplate = $settingsRepo->findBy(['url' => $url]);
221
222
        foreach ($settingsWithTemplate as $s) {
223
            if ($s->getValueTemplate()) {
224
                $templateMap[$s->getVariable()] = $s->getValueTemplate()->getId();
225
            }
226
        }
227
        $platform = [
228
            'server_type' => (string) $manager->getSetting('platform.server_type', true),
229
        ];
230
231
        return $this->render('@ChamiloCore/Admin/Settings/default.html.twig', [
232
            'schemas' => $schemas,
233
            'settings' => $settings,
234
            'form' => $form->createView(),
235
            'keyword' => $keyword,
236
            'search_form' => $this->getSearchForm()->createView(),
237
            'template_map' => $templateMap,
238
            'ordered_namespaces' => $ordered,
239
            'namespace_labels' => $labelMap,
240
            'platform' => $platform,
241
        ]);
242
    }
243
244
    /**
245
     * Sync settings from classes with the database.
246
     */
247
    #[IsGranted('ROLE_ADMIN')]
248
    #[Route('/settings_sync', name: 'sync_settings')]
249
    public function syncSettings(AccessUrlHelper $accessUrlHelper): Response
250
    {
251
        $manager = $this->getSettingsManager();
252
        $url = $accessUrlHelper->getCurrent();
253
        $manager->setUrl($url);
254
        $manager->installSchemas($url);
255
256
        return new Response('Updated');
257
    }
258
259
    #[IsGranted('ROLE_ADMIN')]
260
    #[Route('/settings/template/{id}', name: 'chamilo_platform_settings_template')]
261
    public function getTemplateExample(int $id): JsonResponse
262
    {
263
        $repo = $this->entityManager->getRepository(SettingsValueTemplate::class);
264
        $template = $repo->find($id);
265
266
        if (!$template) {
267
            return $this->json([
268
                'error' => $this->translator->trans('Template not found.'),
269
            ], Response::HTTP_NOT_FOUND);
270
        }
271
272
        return $this->json([
273
            'variable' => $template->getVariable(),
274
            'json_example' => $template->getJsonExample(),
275
            'description' => $template->getDescription(),
276
        ]);
277
    }
278
279
    /**
280
     * @return FormInterface
281
     */
282
    private function getSearchForm()
283
    {
284
        $builder = $this->container->get('form.factory')->createNamedBuilder('search');
285
        $builder->add('keyword', TextType::class);
286
        $builder->add('search', SubmitType::class, ['attr' => ['class' => 'btn btn--primary']]);
287
288
        return $builder->getForm();
289
    }
290
291
    private function computeOrderedNamespacesByTranslatedLabel(array $schemas, Request $request): array
292
    {
293
        $namespaces = array_map(
294
            static fn ($k) => str_replace('chamilo_core.settings.', '', $k),
295
            array_keys($schemas)
296
        );
297
298
        $labelMap = [];
299
        foreach ($namespaces as $ns) {
300
            if ('cas' === $ns) {
301
                $labelMap[$ns] = 'CAS';
302
303
                continue;
304
            }
305
            if ('lp' === $ns) {
306
                $labelMap[$ns] = $this->translator->trans('Learning path');
307
308
                continue;
309
            }
310
            if ('ai_helpers' === $ns) {
311
                $labelMap[$ns] = $this->translator->trans('AI helpers');
312
313
                continue;
314
            }
315
316
            $key = ucfirst(str_replace('_', ' ', $ns));
317
            $labelMap[$ns] = $this->translator->trans($key);
318
        }
319
320
        $collator = class_exists(Collator::class) ? new Collator($request->getLocale()) : null;
321
        usort($namespaces, function ($a, $b) use ($labelMap, $collator) {
322
            return $collator
323
                ? $collator->compare($labelMap[$a], $labelMap[$b])
324
                : strcasecmp($labelMap[$a], $labelMap[$b]);
325
        });
326
327
        $idx = array_search('ai_helpers', $namespaces, true);
328
        if (false !== $idx) {
329
            array_splice($namespaces, $idx, 1);
330
            array_splice($namespaces, 1, 0, ['ai_helpers']);
331
        }
332
333
        return [$namespaces, $labelMap];
334
    }
335
}
336