Passed
Push — master ( cfd449...e324b4 )
by Yannick
08:14
created

computeOrderedNamespacesByTranslatedLabel()   B

Complexity

Conditions 8
Paths 20

Size

Total Lines 40
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

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