Passed
Pull Request — master (#7060)
by Yannick
09:27
created

SettingsManager::getParametersFromKeyword()   B

Complexity

Conditions 6
Paths 12

Size

Total Lines 37
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 21
c 1
b 0
f 0
nc 12
nop 3
dl 0
loc 37
rs 8.9617
1
<?php
2
3
declare(strict_types=1);
4
5
/* For licensing terms, see /license.txt */
6
7
namespace Chamilo\CoreBundle\Settings;
8
9
use Chamilo\CoreBundle\Entity\AccessUrl;
10
use Chamilo\CoreBundle\Entity\Course;
11
use Chamilo\CoreBundle\Entity\SettingsCurrent;
12
use Chamilo\CoreBundle\Helpers\SettingsManagerHelper;
13
use Chamilo\CoreBundle\Search\SearchEngineFieldSynchronizer;
14
use Doctrine\ORM\EntityManager;
15
use Doctrine\ORM\EntityRepository;
16
use InvalidArgumentException;
17
use Sylius\Bundle\SettingsBundle\Manager\SettingsManagerInterface;
18
use Sylius\Bundle\SettingsBundle\Model\Settings;
19
use Sylius\Bundle\SettingsBundle\Model\SettingsInterface;
20
use Sylius\Bundle\SettingsBundle\Registry\ServiceRegistryInterface;
21
use Sylius\Bundle\SettingsBundle\Schema\SchemaInterface;
22
use Sylius\Bundle\SettingsBundle\Schema\SettingsBuilder;
23
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
24
use Symfony\Component\HttpFoundation\RequestStack;
25
use Symfony\Component\Validator\Exception\ValidatorException;
26
27
use const ARRAY_FILTER_USE_KEY;
28
29
/**
30
 * Handles the platform settings.
31
 */
32
class SettingsManager implements SettingsManagerInterface
33
{
34
    protected ?AccessUrl $url = null;
35
36
    protected ServiceRegistryInterface $schemaRegistry;
37
38
    protected EntityManager $manager;
39
40
    protected EntityRepository $repository;
41
42
    protected EventDispatcherInterface $eventDispatcher;
43
44
    /**
45
     * Runtime cache for resolved parameters.
46
     *
47
     * @var Settings[]
48
     */
49
    protected array $resolvedSettings = [];
50
51
    /**
52
     * @var null|array<string, Settings>|mixed[]
53
     */
54
    protected ?array $schemaList;
55
56
    protected RequestStack $request;
57
58
    public function __construct(
59
        ServiceRegistryInterface $schemaRegistry,
60
        EntityManager $manager,
61
        EntityRepository $repository,
62
        EventDispatcherInterface $eventDispatcher,
63
        RequestStack $request,
64
        protected readonly SettingsManagerHelper $settingsManagerHelper,
65
        private readonly SearchEngineFieldSynchronizer $searchEngineFieldSynchronizer,
66
    ) {
67
        $this->schemaRegistry = $schemaRegistry;
68
        $this->manager = $manager;
69
        $this->repository = $repository;
70
        $this->eventDispatcher = $eventDispatcher;
71
        $this->request = $request;
72
        $this->schemaList = [];
73
    }
74
75
    public function getUrl(): ?AccessUrl
76
    {
77
        return $this->url;
78
    }
79
80
    public function setUrl(AccessUrl $url): void
81
    {
82
        $this->url = $url;
83
    }
84
85
    public function updateSchemas(AccessUrl $url): void
86
    {
87
        $this->url = $url;
88
        $schemas = array_keys($this->getSchemas());
89
        foreach ($schemas as $schema) {
90
            $settings = $this->load($this->convertServiceToNameSpace($schema));
91
            $this->update($settings);
92
        }
93
    }
94
95
    public function installSchemas(AccessUrl $url): void
96
    {
97
        $this->url = $url;
98
        $schemas = array_keys($this->getSchemas());
99
        foreach ($schemas as $schema) {
100
            $settings = $this->load($this->convertServiceToNameSpace($schema));
101
            $this->save($settings);
102
        }
103
    }
104
105
    /**
106
     * @return array|AbstractSettingsSchema[]
107
     */
108
    public function getSchemas(): array
109
    {
110
        return $this->schemaRegistry->all();
111
    }
112
113
    public function convertNameSpaceToService(string $category): string
114
    {
115
        return 'chamilo_core.settings.'.$category;
116
    }
117
118
    public function convertServiceToNameSpace(string $category): string
119
    {
120
        return str_replace('chamilo_core.settings.', '', $category);
121
    }
122
123
    public function updateSetting(string $name, $value): void
124
    {
125
        $name = $this->validateSetting($name);
126
127
        [$category, $name] = explode('.', $name);
128
        $settings = $this->load($category);
129
130
        if (!$settings->has($name)) {
131
            $message = \sprintf("Parameter %s doesn't exists.", $name);
132
133
            throw new InvalidArgumentException($message);
134
        }
135
136
        $settings->set($name, $value);
137
        $this->update($settings);
138
    }
139
140
    /**
141
     * Get a specific configuration setting, getting from the previously stored
142
     * PHP session data whenever possible.
143
     *
144
     * @param string $name       The setting name (composed if in a category, i.e. 'platform.institution')
145
     * @param bool   $loadFromDb Whether to load from the database
146
     */
147
    public function getSetting(string $name, bool $loadFromDb = false): mixed
148
    {
149
        $name = $this->validateSetting($name);
150
151
        $overridden = $this->settingsManagerHelper->getOverride($name);
152
153
        if (null !== $overridden) {
154
            return $overridden;
155
        }
156
157
        [$category, $name] = explode('.', $name);
158
159
        if ($loadFromDb) {
160
            $settings = $this->load($category, $name);
161
            if ($settings->has($name)) {
162
                return $settings->get($name);
163
            }
164
165
            return null;
166
        }
167
168
        $this->loadAll();
169
170
        if (!empty($this->schemaList) && isset($this->schemaList[$category])) {
171
            $settings = $this->schemaList[$category];
172
            if ($settings->has($name)) {
173
                return $settings->get($name);
174
            }
175
            error_log("Attempted to access undefined setting '$name' in category '$category'.");
176
177
            return null;
178
        }
179
180
        throw new InvalidArgumentException(\sprintf('Category %s not found', $category));
181
    }
182
183
    public function loadAll(): void
184
    {
185
        $session = null;
186
187
        if ($this->request->getCurrentRequest()) {
188
            $session = $this->request->getCurrentRequest()->getSession();
189
            $schemaList = $session->get('schemas');
190
            if (!empty($schemaList)) {
191
                $this->schemaList = $schemaList;
192
193
                return;
194
            }
195
        }
196
197
        $schemas = array_keys($this->getSchemas());
198
        $schemaList = [];
199
        $settingsBuilder = new SettingsBuilder();
200
        $all = $this->getAllParametersByCategory();
201
202
        foreach ($schemas as $schema) {
203
            $schemaRegister = $this->schemaRegistry->get($schema);
204
            $schemaRegister->buildSettings($settingsBuilder);
205
            $name = $this->convertServiceToNameSpace($schema);
206
            $settings = new Settings();
207
208
            /** @var array<string, mixed> $parameters */
209
            $parameters = $all[$name] ?? [];
210
211
            $knownParameters = array_filter(
212
                $parameters,
213
                fn ($key): bool => $settingsBuilder->isDefined($key),
214
                ARRAY_FILTER_USE_KEY
215
            );
216
217
            $transformers = $settingsBuilder->getTransformers();
218
            foreach ($transformers as $parameter => $transformer) {
219
                if (\array_key_exists($parameter, $knownParameters)) {
220
                    if ('course_creation_use_template' === $parameter) {
221
                        if (empty($knownParameters[$parameter])) {
222
                            $knownParameters[$parameter] = null;
223
                        }
224
                    } else {
225
                        $knownParameters[$parameter] = $transformer->reverseTransform($knownParameters[$parameter]);
226
                    }
227
                }
228
            }
229
230
            $knownParameters = $this->normalizeNullsBeforeResolve($knownParameters, $settingsBuilder);
231
            $parameters = $settingsBuilder->resolve($knownParameters);
232
            $settings->setParameters($parameters);
233
            $schemaList[$name] = $settings;
234
        }
235
        $this->schemaList = $schemaList;
236
        if ($session && $this->request->getCurrentRequest()) {
237
            $session->set('schemas', $schemaList);
238
        }
239
    }
240
241
    public function load(string $schemaAlias, ?string $namespace = null, bool $ignoreUnknown = true): SettingsInterface
242
    {
243
        $settings = new Settings();
244
        $schemaAliasNoPrefix = $schemaAlias;
245
        $schemaAlias = 'chamilo_core.settings.'.$schemaAlias;
246
        if ($this->schemaRegistry->has($schemaAlias)) {
247
            /** @var SchemaInterface $schema */
248
            $schema = $this->schemaRegistry->get($schemaAlias);
249
        } else {
250
            return $settings;
251
        }
252
253
        $settings->setSchemaAlias($schemaAlias);
254
255
        // We need to get a plain parameters array since we use the options resolver on it
256
        $parameters = $this->getParameters($schemaAliasNoPrefix);
257
        $settingsBuilder = new SettingsBuilder();
258
        $schema->buildSettings($settingsBuilder);
259
260
        // Remove unknown settings' parameters (e.g. From a previous version of the settings schema)
261
        if (true === $ignoreUnknown) {
262
            foreach ($parameters as $name => $value) {
263
                if (!$settingsBuilder->isDefined($name)) {
264
                    unset($parameters[$name]);
265
                }
266
            }
267
        }
268
269
        foreach ($settingsBuilder->getTransformers() as $parameter => $transformer) {
270
            if (\array_key_exists($parameter, $parameters)) {
271
                $parameters[$parameter] = $transformer->reverseTransform($parameters[$parameter]);
272
            }
273
        }
274
        $parameters = $this->normalizeNullsBeforeResolve($parameters, $settingsBuilder);
275
        $parameters = $settingsBuilder->resolve($parameters);
276
        $settings->setParameters($parameters);
277
278
        return $settings;
279
    }
280
281
    public function update(SettingsInterface $settings): void
282
    {
283
        $namespace = $settings->getSchemaAlias();
284
285
        /** @var SchemaInterface $schema */
286
        $schema = $this->schemaRegistry->get($settings->getSchemaAlias());
287
288
        $settingsBuilder = new SettingsBuilder();
289
        $schema->buildSettings($settingsBuilder);
290
        $raw = $settings->getParameters();
291
        $raw = $this->normalizeNullsBeforeResolve($raw, $settingsBuilder);
292
        $parameters = $settingsBuilder->resolve($raw);
293
294
        foreach ($parameters as $parameter => $value) {
295
            $parameters[$parameter] = $this->transformToString($value);
296
        }
297
298
        $settings->setParameters($parameters);
299
        $category = $this->convertServiceToNameSpace($settings->getSchemaAlias());
300
301
        // Restrict lookup to current URL so we do not override settings from other URLs.
302
        $criteria = [
303
            'category' => $category,
304
        ];
305
306
        if (null !== $this->url) {
307
            $criteria['url'] = $this->url;
308
        }
309
310
        $persistedParameters = $this->repository->findBy($criteria);
311
312
        $persistedParametersMap = [];
313
314
        /** @var SettingsCurrent $parameter */
315
        foreach ($persistedParameters as $parameter) {
316
            $persistedParametersMap[$parameter->getVariable()] = $parameter;
317
        }
318
319
        $url = $this->getUrl();
320
        $simpleCategoryName = str_replace('chamilo_core.settings.', '', $namespace);
321
322
        foreach ($parameters as $name => $value) {
323
            // MultiURL: respect access_url_changeable defined on main URL.
324
            if (!$this->isSettingChangeableForCurrentUrl($simpleCategoryName, $name)) {
325
                continue;
326
            }
327
328
            if (isset($persistedParametersMap[$name])) {
329
                $parameter = $persistedParametersMap[$name];
330
                $parameter->setSelectedValue($value);
331
                $parameter->setCategory($simpleCategoryName);
332
                $this->manager->persist($parameter);
333
            } else {
334
                $parameter = $this->createSettingForCurrentUrl($simpleCategoryName, $name, $value);
335
                $this->manager->persist($parameter);
336
            }
337
        }
338
339
        $this->applySearchEngineFieldsSyncIfNeeded($simpleCategoryName, $parameters);
340
341
        $this->manager->flush();
342
    }
343
344
    /**
345
     * @throws ValidatorException
346
     */
347
    public function save(SettingsInterface $settings): void
348
    {
349
        $namespace = $settings->getSchemaAlias();
350
351
        /** @var SchemaInterface $schema */
352
        $schema = $this->schemaRegistry->get($settings->getSchemaAlias());
353
354
        $settingsBuilder = new SettingsBuilder();
355
        $schema->buildSettings($settingsBuilder);
356
        $raw = $settings->getParameters();
357
        $raw = $this->normalizeNullsBeforeResolve($raw, $settingsBuilder);
358
        $parameters = $settingsBuilder->resolve($raw);
359
360
        foreach ($parameters as $parameter => $value) {
361
            $parameters[$parameter] = $this->transformToString($value);
362
        }
363
        $settings->setParameters($parameters);
364
        $criteria = [
365
            'category' => $this->convertServiceToNameSpace($settings->getSchemaAlias()),
366
        ];
367
368
        // Limit to current URL so we do not write across all URLs.
369
        if (null !== $this->url) {
370
            $criteria['url'] = $this->url;
371
        }
372
373
        $persistedParameters = $this->repository->findBy($criteria);
374
        $persistedParametersMap = [];
375
        foreach ($persistedParameters as $parameter) {
376
            $persistedParametersMap[$parameter->getVariable()] = $parameter;
377
        }
378
379
        $url = $this->getUrl();
380
        $simpleCategoryName = str_replace('chamilo_core.settings.', '', $namespace);
381
382
        foreach ($parameters as $name => $value) {
383
            // MultiURL: respect access_url_changeable defined on main URL.
384
            if (!$this->isSettingChangeableForCurrentUrl($simpleCategoryName, $name)) {
385
                continue;
386
            }
387
388
            if (isset($persistedParametersMap[$name])) {
389
                $parameter = $persistedParametersMap[$name];
390
                $parameter->setSelectedValue($value);
391
            } else {
392
                $parameter = $this->createSettingForCurrentUrl($simpleCategoryName, $name, $value);
393
                $this->manager->persist($parameter);
394
            }
395
        }
396
397
        $this->applySearchEngineFieldsSyncIfNeeded($simpleCategoryName, $parameters);
398
399
        $this->manager->flush();
400
    }
401
402
    /**
403
     * Sync JSON-defined search fields into search_engine_field table.
404
     */
405
    private function applySearchEngineFieldsSyncIfNeeded(string $category, array $parameters): void
406
    {
407
        if ('search' !== $category) {
408
            return;
409
        }
410
411
        if (!\array_key_exists('search_prefilter_prefix', $parameters)) {
412
            return;
413
        }
414
415
        $json = (string) $parameters['search_prefilter_prefix'];
416
417
        // Non-destructive by default (no deletes)
418
        $this->searchEngineFieldSynchronizer->syncFromJson($json, true);
419
    }
420
421
    /**
422
     * @param string $keyword
423
     */
424
    public function getParametersFromKeywordOrderedByCategory($keyword): array
425
    {
426
        $qb = $this->repository->createQueryBuilder('s')
427
            ->where('s.variable LIKE :keyword OR s.title LIKE :keyword')
428
            ->setParameter('keyword', "%{$keyword}%");
429
430
        // Restrict search to current URL when available.
431
        if (null !== $this->url) {
432
            $qb
433
                ->andWhere('s.url = :url')
434
                ->setParameter('url', $this->url);
435
        }
436
437
        $parametersFromDb = $qb->getQuery()->getResult();
438
        $parameters = [];
439
440
        foreach ($parametersFromDb as $parameter) {
441
            /** @var SettingsCurrent $parameter */
442
            $category = $parameter->getCategory();
443
            $variable = $parameter->getVariable();
444
445
            $hidden = [];
446
            $serviceKey = 'chamilo_core.settings.'.$category;
447
            if ($this->schemaRegistry->has($serviceKey)) {
448
                $schema = $this->schemaRegistry->get($serviceKey);
449
                if (method_exists($schema, 'getHiddenSettings')) {
450
                    $hidden = $schema->getHiddenSettings();
451
                }
452
            }
453
454
            if (\in_array($variable, $hidden, true)) {
455
                continue;
456
            }
457
458
            $parameters[$category][] = $parameter;
459
        }
460
461
        return $parameters;
462
    }
463
464
    /**
465
     * @param string $namespace
466
     * @param string $keyword
467
     * @param bool   $returnObjects
468
     *
469
     * @return array
470
     */
471
    public function getParametersFromKeyword($namespace, $keyword = '', $returnObjects = false)
472
    {
473
        if (empty($keyword)) {
474
            $criteria = [
475
                'category' => $namespace,
476
            ];
477
478
            if (null !== $this->url) {
479
                $criteria['url'] = $this->url;
480
            }
481
482
            $parametersFromDb = $this->repository->findBy($criteria);
483
        } else {
484
            $qb = $this->repository->createQueryBuilder('s')
485
                ->where('s.variable LIKE :keyword')
486
                ->setParameter('keyword', "%{$keyword}%");
487
488
            if (null !== $this->url) {
489
                $qb
490
                    ->andWhere('s.url = :url')
491
                    ->setParameter('url', $this->url);
492
            }
493
494
            $parametersFromDb = $qb->getQuery()->getResult();
495
        }
496
497
        if ($returnObjects) {
498
            return $parametersFromDb;
499
        }
500
        $parameters = [];
501
502
        /** @var SettingsCurrent $parameter */
503
        foreach ($parametersFromDb as $parameter) {
504
            $parameters[$parameter->getVariable()] = $parameter->getSelectedValue();
505
        }
506
507
        return $parameters;
508
    }
509
510
    private function validateSetting(string $name): string
511
    {
512
        if (!str_contains($name, '.')) {
513
            // throw new \InvalidArgumentException(sprintf('Parameter must be in format "namespace.name", "%s" given.', $name));
514
515
            // This code allows the possibility of calling
516
            // api_get_setting('allow_skills_tool') instead of
517
            // the "correct" way api_get_setting('platform.allow_skills_tool')
518
            $items = $this->getVariablesAndCategories();
519
520
            if (isset($items[$name])) {
521
                $originalName = $name;
522
                $name = $this->renameVariable($name);
523
                $category = $this->fixCategory(
524
                    strtolower($name),
525
                    strtolower($items[$originalName])
526
                );
527
                $name = $category.'.'.$name;
528
            } else {
529
                $message = \sprintf('Parameter must be in format "category.name", "%s" given.', $name);
530
531
                throw new InvalidArgumentException($message);
532
            }
533
        }
534
535
        return $name;
536
    }
537
538
    /**
539
     * Load parameter from database.
540
     *
541
     * @param string $namespace
542
     *
543
     * @return array
544
     */
545
    private function getParameters($namespace)
546
    {
547
        $parameters = [];
548
549
        $criteria = ['category' => $namespace];
550
551
        // MultiURL: only parameters for current URL (if set).
552
        if (null !== $this->url) {
553
            $criteria['url'] = $this->url;
554
        }
555
556
        $category = $this->repository->findBy($criteria);
557
558
        /** @var SettingsCurrent $parameter */
559
        foreach ($category as $parameter) {
560
            $parameters[$parameter->getVariable()] = $parameter->getSelectedValue();
561
        }
562
563
        return $parameters;
564
    }
565
566
    private function getAllParametersByCategory()
567
    {
568
        $parameters = [];
569
570
        // MultiURL: either all parameters (single URL mode) or only for current URL.
571
        if (null !== $this->url) {
572
            $all = $this->repository->findBy(['url' => $this->url]);
573
        } else {
574
            $all = $this->repository->findAll();
575
        }
576
577
        /** @var SettingsCurrent $parameter */
578
        foreach ($all as $parameter) {
579
            $parameters[$parameter->getCategory()][$parameter->getVariable()] = $parameter->getSelectedValue();
580
        }
581
582
        return $parameters;
583
    }
584
585
    /**
586
     * Check if a setting is changeable for the current URL, using the
587
     * access_url_changeable flag from the main URL (ID = 1).
588
     */
589
    private function isSettingChangeableForCurrentUrl(string $category, string $variable): bool
590
    {
591
        // No URL bound: behave as legacy single-URL platform.
592
        if (null === $this->url) {
593
            return true;
594
        }
595
596
        // Main URL can always edit settings. UI already restricts who can see/edit fields.
597
        if (1 === $this->url->getId()) {
598
            return true;
599
        }
600
601
        // Try to load main (canonical) URL.
602
        $mainUrl = $this->manager->getRepository(AccessUrl::class)->find(1);
603
        if (null === $mainUrl) {
604
            // If main URL is missing, fallback to permissive behaviour.
605
            return true;
606
        }
607
608
        /** @var SettingsCurrent|null $mainSetting */
609
        $mainSetting = $this->repository->findOneBy([
610
            'category' => $category,
611
            'variable' => $variable,
612
            'url' => $mainUrl,
613
        ]);
614
615
        if (null === $mainSetting) {
616
            // If there is no canonical row, do not block changes.
617
            return true;
618
        }
619
620
        // When access_url_changeable is false/0 on main URL,
621
        // secondary URLs must not override the value.
622
        return (bool) $mainSetting->getAccessUrlChangeable();
623
    }
624
625
    private function createSettingForCurrentUrl(string $category, string $variable, string $value): SettingsCurrent
626
    {
627
        $url = $this->getUrl();
628
629
        // Try to reuse metadata from main URL (ID = 1) as canonical definition.
630
        $mainUrl = $this->manager->getRepository(AccessUrl::class)->find(1);
631
632
        $reference = null;
633
634
        if (null !== $mainUrl) {
635
            $reference = $this->repository->findOneBy([
636
                'category' => $category,
637
                'variable' => $variable,
638
                'url' => $mainUrl,
639
            ]);
640
        }
641
642
        if (!$reference instanceof SettingsCurrent) {
643
            // Fallback: any existing row for this category + variable (legacy / no-URL case).
644
            $reference = $this->repository->findOneBy([
645
                'category' => $category,
646
                'variable' => $variable,
647
            ]);
648
        }
649
650
        $setting = (new SettingsCurrent())
651
            ->setVariable($variable)
652
            ->setCategory($category)
653
            ->setSelectedValue($value)
654
            ->setUrl($url)
655
        ;
656
657
        if ($reference instanceof SettingsCurrent) {
658
            // Copy descriptive metadata so the new URL row behaves like the canonical one.
659
            $setting->setTitle($reference->getTitle());
660
661
            // These fields may or may not exist in the entity in Chamilo 2,
662
            // so we check for method existence to stay safe.
663
            if (method_exists($setting, 'setType') && method_exists($reference, 'getType')) {
664
                $setting->setType($reference->getType());
665
            }
666
667
            if (method_exists($setting, 'setComment') && method_exists($reference, 'getComment')) {
668
                $setting->setComment($reference->getComment());
669
            }
670
671
            if (method_exists($setting, 'setScope') && method_exists($reference, 'getScope')) {
672
                $setting->setScope($reference->getScope());
673
            }
674
675
            if (method_exists($setting, 'setSubkey') && method_exists($reference, 'getSubkey')) {
676
                $setting->setSubkey($reference->getSubkey());
677
            }
678
679
            if (method_exists($setting, 'setSubkeytext') && method_exists($reference, 'getSubkeytext')) {
680
                $setting->setSubkeytext($reference->getSubkeytext());
681
            }
682
683
            // Copy flags and template; the "changeable" flag is still interpreted from main URL.
684
            $setting->setAccessUrlChangeable($reference->getAccessUrlChangeable());
685
            $setting->setAccessUrlLocked($reference->getAccessUrlLocked());
686
687
            if (method_exists($setting, 'setValueTemplate') && method_exists($reference, 'getValueTemplate')) {
688
                $setting->setValueTemplate($reference->getValueTemplate());
689
            }
690
        } else {
691
            // Fallback: minimal metadata if no canonical definition was found.
692
            $setting
693
                ->setTitle($variable)
694
                ->setAccessUrlChangeable(1)
695
                ->setAccessUrlLocked(1)
696
            ;
697
        }
698
699
        return $setting;
700
    }
701
702
    /*private function transformParameters(SettingsBuilder $settingsBuilder, array $parameters)
703
     * {
704
     * $transformedParameters = $parameters;
705
     * foreach ($settingsBuilder->getTransformers() as $parameter => $transformer) {
706
     * if (array_key_exists($parameter, $parameters)) {
707
     * $transformedParameters[$parameter] = $transformer->reverseTransform($parameters[$parameter]);
708
     * }
709
     * }
710
     * return $transformedParameters;
711
     * }*/
712
713
    /**
714
     * Get variables and categories as in 1.11.x.
715
     */
716
    private function getVariablesAndCategories(): array
717
    {
718
        return [
719
            'Institution' => 'Platform',
720
            'InstitutionUrl' => 'Platform',
721
            'siteName' => 'Platform',
722
            'site_name' => 'Platform',
723
            'emailAdministrator' => 'admin',
724
            // 'emailAdministrator' => 'Platform',
725
            'administratorSurname' => 'admin',
726
            'administratorTelephone' => 'admin',
727
            'administratorName' => 'admin',
728
            'show_administrator_data' => 'Platform',
729
            'show_tutor_data' => 'Session',
730
            'show_teacher_data' => 'Platform',
731
            'show_toolshortcuts' => 'Course',
732
            'allow_group_categories' => 'Course',
733
            'server_type' => 'Platform',
734
            'platformLanguage' => 'Language',
735
            'showonline' => 'Platform',
736
            'profile' => 'User',
737
            'default_document_quotum' => 'Course',
738
            'registration' => 'User',
739
            'default_group_quotum' => 'Course',
740
            'allow_registration' => 'Platform',
741
            'allow_registration_as_teacher' => 'Platform',
742
            'allow_lostpassword' => 'Platform',
743
            'allow_user_headings' => 'Course',
744
            'allow_personal_agenda' => 'agenda',
745
            'display_coursecode_in_courselist' => 'Platform',
746
            'display_teacher_in_courselist' => 'Platform',
747
            'permanently_remove_deleted_files' => 'Tools',
748
            'dropbox_allow_overwrite' => 'Tools',
749
            'dropbox_max_filesize' => 'Tools',
750
            'dropbox_allow_just_upload' => 'Tools',
751
            'dropbox_allow_student_to_student' => 'Tools',
752
            'dropbox_allow_group' => 'Tools',
753
            'dropbox_allow_mailing' => 'Tools',
754
            'extended_profile' => 'User',
755
            'student_view_enabled' => 'Platform',
756
            'show_navigation_menu' => 'Course',
757
            'enable_tool_introduction' => 'course',
758
            'page_after_login' => 'Platform',
759
            'time_limit_whosonline' => 'Platform',
760
            'breadcrumbs_course_homepage' => 'Course',
761
            'example_material_course_creation' => 'Platform',
762
            'account_valid_duration' => 'Platform',
763
            'use_session_mode' => 'Session',
764
            'allow_email_editor' => 'Tools',
765
            // 'registered' => null',
766
            // 'donotlistcampus' =>'null',
767
            'show_email_addresses' => 'Platform',
768
            'service_ppt2lp' => 'NULL',
769
            'upload_extensions_list_type' => 'Security',
770
            'upload_extensions_blacklist' => 'Security',
771
            'upload_extensions_whitelist' => 'Security',
772
            'upload_extensions_skip' => 'Security',
773
            'upload_extensions_replace_by' => 'Security',
774
            'show_number_of_courses' => 'Platform',
775
            'show_empty_course_categories' => 'Platform',
776
            'show_back_link_on_top_of_tree' => 'Platform',
777
            'show_different_course_language' => 'Platform',
778
            'split_users_upload_directory' => 'Tuning',
779
            'display_categories_on_homepage' => 'Platform',
780
            'permissions_for_new_directories' => 'Security',
781
            'permissions_for_new_files' => 'Security',
782
            'show_tabs' => 'Platform',
783
            'default_forum_view' => 'Course',
784
            'platform_charset' => 'Languages',
785
            'survey_email_sender_noreply' => 'Course',
786
            'gradebook_enable' => 'Gradebook',
787
            'gradebook_score_display_coloring' => 'Gradebook',
788
            'gradebook_score_display_custom' => 'Gradebook',
789
            'gradebook_score_display_colorsplit' => 'Gradebook',
790
            'gradebook_score_display_upperlimit' => 'Gradebook',
791
            'gradebook_number_decimals' => 'Gradebook',
792
            'user_selected_theme' => 'Platform',
793
            'allow_course_theme' => 'Course',
794
            'show_closed_courses' => 'Platform',
795
            'extendedprofile_registration' => 'User',
796
            'extendedprofile_registrationrequired' => 'User',
797
            'add_users_by_coach' => 'Session',
798
            'extend_rights_for_coach' => 'Security',
799
            'extend_rights_for_coach_on_survey' => 'Security',
800
            'course_create_active_tools' => 'Tools',
801
            'show_session_coach' => 'Session',
802
            'allow_users_to_create_courses' => 'Platform',
803
            'allow_message_tool' => 'Tools',
804
            'allow_social_tool' => 'Tools',
805
            'show_session_data' => 'Session',
806
            'allow_use_sub_language' => 'language',
807
            'show_glossary_in_documents' => 'Course',
808
            'allow_terms_conditions' => 'Platform',
809
            'search_enabled' => 'Search',
810
            'search_prefilter_prefix' => 'Search',
811
            'search_show_unlinked_results' => 'Search',
812
            'allow_coach_to_edit_course_session' => 'Session',
813
            'show_glossary_in_extra_tools' => 'Course',
814
            'send_email_to_admin_when_create_course' => 'Platform',
815
            'go_to_course_after_login' => 'Course',
816
            'math_asciimathML' => 'Editor',
817
            'enabled_asciisvg' => 'Editor',
818
            'include_asciimathml_script' => 'Editor',
819
            'youtube_for_students' => 'Editor',
820
            'block_copy_paste_for_students' => 'Editor',
821
            'more_buttons_maximized_mode' => 'Editor',
822
            'students_download_folders' => 'Document',
823
            'users_copy_files' => 'Tools',
824
            'allow_students_to_create_groups_in_social' => 'Tools',
825
            'allow_send_message_to_all_platform_users' => 'Message',
826
            'message_max_upload_filesize' => 'Tools',
827
            'use_users_timezone' => 'profile',
828
            // 'use_users_timezone' => 'Timezones',
829
            'timezone_value' => 'platform',
830
            // 'timezone_value' => 'Timezones',
831
            'allow_user_course_subscription_by_course_admin' => 'Security',
832
            'show_link_bug_notification' => 'Platform',
833
            'show_link_ticket_notification' => 'Platform',
834
            'course_validation' => 'course',
835
            // 'course_validation' => 'Platform',
836
            'course_validation_terms_and_conditions_url' => 'Platform',
837
            'enabled_wiris' => 'Editor',
838
            'allow_spellcheck' => 'Editor',
839
            'force_wiki_paste_as_plain_text' => 'Editor',
840
            'enabled_googlemaps' => 'Editor',
841
            'enabled_imgmap' => 'Editor',
842
            'enabled_support_svg' => 'Tools',
843
            'pdf_export_watermark_enable' => 'Platform',
844
            'pdf_export_watermark_by_course' => 'Platform',
845
            'pdf_export_watermark_text' => 'Platform',
846
            'enabled_insertHtml' => 'Editor',
847
            'students_export2pdf' => 'Document',
848
            'exercise_min_score' => 'Course',
849
            'exercise_max_score' => 'Course',
850
            'show_users_folders' => 'Tools',
851
            'show_default_folders' => 'Tools',
852
            'show_chat_folder' => 'Tools',
853
            'course_hide_tools' => 'Course',
854
            'show_groups_to_users' => 'Group',
855
            'accessibility_font_resize' => 'Platform',
856
            'hide_courses_in_sessions' => 'Session',
857
            'enable_quiz_scenario' => 'Course',
858
            'filter_terms' => 'Security',
859
            'header_extra_content' => 'Tracking',
860
            'footer_extra_content' => 'Tracking',
861
            'show_documents_preview' => 'Tools',
862
            'htmlpurifier_wiki' => 'Editor',
863
            'cas_activate' => 'CAS',
864
            'cas_server' => 'CAS',
865
            'cas_server_uri' => 'CAS',
866
            'cas_port' => 'CAS',
867
            'cas_protocol' => 'CAS',
868
            'cas_add_user_activate' => 'CAS',
869
            'update_user_info_cas_with_ldap' => 'CAS',
870
            'student_page_after_login' => 'Platform',
871
            'teacher_page_after_login' => 'Platform',
872
            'drh_page_after_login' => 'Platform',
873
            'sessionadmin_page_after_login' => 'Session',
874
            'student_autosubscribe' => 'Platform',
875
            'teacher_autosubscribe' => 'Platform',
876
            'drh_autosubscribe' => 'Platform',
877
            'sessionadmin_autosubscribe' => 'Session',
878
            'scorm_cumulative_session_time' => 'Course',
879
            'allow_hr_skills_management' => 'Gradebook',
880
            'enable_help_link' => 'Platform',
881
            'teachers_can_change_score_settings' => 'Gradebook',
882
            'allow_users_to_change_email_with_no_password' => 'User',
883
            'show_admin_toolbar' => 'display',
884
            'allow_global_chat' => 'Platform',
885
            'languagePriority1' => 'language',
886
            'languagePriority2' => 'language',
887
            'languagePriority3' => 'language',
888
            'languagePriority4' => 'language',
889
            'login_is_email' => 'Platform',
890
            'courses_default_creation_visibility' => 'Course',
891
            'gradebook_enable_grade_model' => 'Gradebook',
892
            'teachers_can_change_grade_model_settings' => 'Gradebook',
893
            'gradebook_default_weight' => 'Gradebook',
894
            'ldap_description' => 'LDAP',
895
            'shibboleth_description' => 'Shibboleth',
896
            'facebook_description' => 'Facebook',
897
            'gradebook_locking_enabled' => 'Gradebook',
898
            'gradebook_default_grade_model_id' => 'Gradebook',
899
            'allow_session_admins_to_manage_all_sessions' => 'Session',
900
            'allow_skills_tool' => 'Platform',
901
            'allow_public_certificates' => 'Course',
902
            'platform_unsubscribe_allowed' => 'Platform',
903
            'enable_iframe_inclusion' => 'Editor',
904
            'show_hot_courses' => 'Platform',
905
            'enable_webcam_clip' => 'Tools',
906
            'use_custom_pages' => 'Platform',
907
            'tool_visible_by_default_at_creation' => 'Tools',
908
            'prevent_session_admins_to_manage_all_users' => 'Session',
909
            'documents_default_visibility_defined_in_course' => 'Tools',
910
            'enabled_mathjax' => 'Editor',
911
            'meta_twitter_site' => 'Tracking',
912
            'meta_twitter_creator' => 'Tracking',
913
            'meta_title' => 'Tracking',
914
            'meta_description' => 'Tracking',
915
            'meta_image_path' => 'Tracking',
916
            'allow_teachers_to_create_sessions' => 'Session',
917
            'institution_address' => 'Platform',
918
            'chamilo_database_version' => 'null',
919
            'cron_remind_course_finished_activate' => 'Crons',
920
            'cron_remind_course_expiration_frequency' => 'Crons',
921
            'cron_remind_course_expiration_activate' => 'Crons',
922
            'allow_coach_feedback_exercises' => 'Session',
923
            'allow_my_files' => 'Platform',
924
            'ticket_allow_student_add' => 'Ticket',
925
            'ticket_send_warning_to_all_admins' => 'Ticket',
926
            'ticket_warn_admin_no_user_in_category' => 'Ticket',
927
            'ticket_allow_category_edition' => 'Ticket',
928
            'load_term_conditions_section' => 'Platform',
929
            'show_terms_if_profile_completed' => 'Profile',
930
            'hide_home_top_when_connected' => 'Platform',
931
            'hide_global_announcements_when_not_connected' => 'Platform',
932
            'course_creation_use_template' => 'Course',
933
            'allow_strength_pass_checker' => 'Security',
934
            'allow_captcha' => 'Security',
935
            'captcha_number_mistakes_to_block_account' => 'Security',
936
            'captcha_time_to_block' => 'Security',
937
            'drh_can_access_all_session_content' => 'Session',
938
            'display_groups_forum_in_general_tool' => 'Tools',
939
            'allow_tutors_to_assign_students_to_session' => 'Session',
940
            'allow_lp_return_link' => 'Course',
941
            'hide_scorm_export_link' => 'Course',
942
            'hide_scorm_copy_link' => 'Course',
943
            'hide_scorm_pdf_link' => 'Course',
944
            'session_days_before_coach_access' => 'Session',
945
            'session_days_after_coach_access' => 'Session',
946
            'pdf_logo_header' => 'Course',
947
            'order_user_list_by_official_code' => 'Platform',
948
            'email_alert_manager_on_new_quiz' => 'exercise',
949
            'show_official_code_exercise_result_list' => 'Tools',
950
            'auto_detect_language_custom_pages' => 'Platform',
951
            'lp_show_reduced_report' => 'Course',
952
            'allow_session_course_copy_for_teachers' => 'Session',
953
            'hide_logout_button' => 'Platform',
954
            'redirect_admin_to_courses_list' => 'Platform',
955
            'course_images_in_courses_list' => 'Course',
956
            'student_publication_to_take_in_gradebook' => 'Gradebook',
957
            'certificate_filter_by_official_code' => 'Gradebook',
958
            'exercise_max_ckeditors_in_page' => 'Tools',
959
            'document_if_file_exists_option' => 'Tools',
960
            'add_gradebook_certificates_cron_task_enabled' => 'Gradebook',
961
            'openbadges_backpack' => 'Gradebook',
962
            'cookie_warning' => 'Tools',
963
            'hide_course_group_if_no_tools_available' => 'Tools',
964
            'registration.soap.php.decode_utf8' => 'Platform',
965
            'allow_delete_attendance' => 'Tools',
966
            'gravatar_enabled' => 'Platform',
967
            'gravatar_type' => 'Platform',
968
            'limit_session_admin_role' => 'Session',
969
            'show_session_description' => 'Session',
970
            'hide_certificate_export_link_students' => 'Gradebook',
971
            'hide_certificate_export_link' => 'Gradebook',
972
            'dropbox_hide_course_coach' => 'Tools',
973
            'dropbox_hide_general_coach' => 'Tools',
974
            'session_course_ordering' => 'Session',
975
            'gamification_mode' => 'Platform',
976
            'prevent_multiple_simultaneous_login' => 'Security',
977
            'gradebook_detailed_admin_view' => 'Gradebook',
978
            'user_reset_password' => 'Security',
979
            'user_reset_password_token_limit' => 'Security',
980
            'my_courses_view_by_session' => 'Session',
981
            'show_full_skill_name_on_skill_wheel' => 'Platform',
982
            'messaging_allow_send_push_notification' => 'WebServices',
983
            'messaging_gdc_project_number' => 'WebServices',
984
            'messaging_gdc_api_key' => 'WebServices',
985
            'teacher_can_select_course_template' => 'Course',
986
            'allow_show_skype_account' => 'Platform',
987
            'allow_show_linkedin_url' => 'Platform',
988
            'enable_profile_user_address_geolocalization' => 'User',
989
            'show_official_code_whoisonline' => 'Profile',
990
            'icons_mode_svg' => 'display',
991
            'default_calendar_view' => 'agenda',
992
            'exercise_invisible_in_session' => 'exercise',
993
            'configure_exercise_visibility_in_course' => 'exercise',
994
            'allow_download_documents_by_api_key' => 'Webservices',
995
            'profiling_filter_adding_users' => 'course',
996
            'donotlistcampus' => 'platform',
997
            'course_creation_splash_screen' => 'Course',
998
            'translate_html' => 'Editor',
999
        ];
1000
    }
1001
1002
    /**
1003
     * Rename old variable with variable used in Chamilo 2.0.
1004
     *
1005
     * @param string $variable
1006
     */
1007
    private function renameVariable($variable)
1008
    {
1009
        $list = [
1010
            'timezone_value' => 'timezone',
1011
            'Institution' => 'institution',
1012
            'SiteName' => 'site_name',
1013
            'siteName' => 'site_name',
1014
            'InstitutionUrl' => 'institution_url',
1015
            'registration' => 'required_profile_fields',
1016
            'platformLanguage' => 'platform_language',
1017
            'languagePriority1' => 'language_priority_1',
1018
            'languagePriority2' => 'language_priority_2',
1019
            'languagePriority3' => 'language_priority_3',
1020
            'languagePriority4' => 'language_priority_4',
1021
            'gradebook_score_display_coloring' => 'my_display_coloring',
1022
            'ProfilingFilterAddingUsers' => 'profiling_filter_adding_users',
1023
            'course_create_active_tools' => 'active_tools_on_create',
1024
            'emailAdministrator' => 'administrator_email',
1025
            'administratorSurname' => 'administrator_surname',
1026
            'administratorName' => 'administrator_name',
1027
            'administratorTelephone' => 'administrator_phone',
1028
            'registration.soap.php.decode_utf8' => 'decode_utf8',
1029
            'profile' => 'changeable_options',
1030
        ];
1031
1032
        return $list[$variable] ?? $variable;
1033
    }
1034
1035
    /**
1036
     * Replace old Chamilo 1.x category with 2.0 version.
1037
     *
1038
     * @param string $variable
1039
     * @param string $defaultCategory
1040
     */
1041
    private function fixCategory($variable, $defaultCategory)
1042
    {
1043
        $settings = [
1044
            'cookie_warning' => 'platform',
1045
            'donotlistcampus' => 'platform',
1046
            'administrator_email' => 'admin',
1047
            'administrator_surname' => 'admin',
1048
            'administrator_name' => 'admin',
1049
            'administrator_phone' => 'admin',
1050
            'exercise_max_ckeditors_in_page' => 'exercise',
1051
            'allow_hr_skills_management' => 'skill',
1052
            'accessibility_font_resize' => 'display',
1053
            'account_valid_duration' => 'profile',
1054
            'allow_global_chat' => 'chat',
1055
            'allow_lostpassword' => 'registration',
1056
            'allow_registration' => 'registration',
1057
            'allow_registration_as_teacher' => 'registration',
1058
            'required_profile_fields' => 'registration',
1059
            'allow_skills_tool' => 'skill',
1060
            'allow_terms_conditions' => 'registration',
1061
            'allow_users_to_create_courses' => 'course',
1062
            'auto_detect_language_custom_pages' => 'language',
1063
            'platform_language' => 'language',
1064
            'course_validation' => 'course',
1065
            'course_validation_terms_and_conditions_url' => 'course',
1066
            'display_categories_on_homepage' => 'display',
1067
            'display_coursecode_in_courselist' => 'course',
1068
            'display_teacher_in_courselist' => 'course',
1069
            'drh_autosubscribe' => 'registration',
1070
            'drh_page_after_login' => 'registration',
1071
            'enable_help_link' => 'display',
1072
            'example_material_course_creation' => 'course',
1073
            'login_is_email' => 'profile',
1074
            'noreply_email_address' => 'mail',
1075
            'pdf_export_watermark_by_course' => 'document',
1076
            'pdf_export_watermark_enable' => 'document',
1077
            'pdf_export_watermark_text' => 'document',
1078
            'platform_unsubscribe_allowed' => 'registration',
1079
            'send_email_to_admin_when_create_course' => 'course',
1080
            'show_admin_toolbar' => 'display',
1081
            'show_administrator_data' => 'display',
1082
            'show_back_link_on_top_of_tree' => 'display',
1083
            'show_closed_courses' => 'display',
1084
            'show_different_course_language' => 'display',
1085
            'show_email_addresses' => 'display',
1086
            'show_empty_course_categories' => 'display',
1087
            'show_full_skill_name_on_skill_wheel' => 'skill',
1088
            'show_hot_courses' => 'display',
1089
            'show_link_bug_notification' => 'display',
1090
            'show_number_of_courses' => 'display',
1091
            'show_teacher_data' => 'display',
1092
            'showonline' => 'display',
1093
            'student_autosubscribe' => 'registration',
1094
            'student_page_after_login' => 'registration',
1095
            'student_view_enabled' => 'course',
1096
            'teacher_autosubscribe' => 'registration',
1097
            'teacher_page_after_login' => 'registration',
1098
            'time_limit_whosonline' => 'display',
1099
            'user_selected_theme' => 'profile',
1100
            'hide_global_announcements_when_not_connected' => 'announcement',
1101
            'hide_home_top_when_connected' => 'display',
1102
            'hide_logout_button' => 'display',
1103
            'institution_address' => 'platform',
1104
            'redirect_admin_to_courses_list' => 'admin',
1105
            'use_custom_pages' => 'platform',
1106
            'allow_group_categories' => 'group',
1107
            'allow_user_headings' => 'display',
1108
            'default_document_quotum' => 'document',
1109
            'default_forum_view' => 'forum',
1110
            'default_group_quotum' => 'document',
1111
            'enable_quiz_scenario' => 'exercise',
1112
            'exercise_max_score' => 'exercise',
1113
            'exercise_min_score' => 'exercise',
1114
            'pdf_logo_header' => 'platform',
1115
            'show_glossary_in_extra_tools' => 'glossary',
1116
            'survey_email_sender_noreply' => 'survey',
1117
            'allow_coach_feedback_exercises' => 'exercise',
1118
            'sessionadmin_autosubscribe' => 'registration',
1119
            'sessionadmin_page_after_login' => 'registration',
1120
            'show_tutor_data' => 'display',
1121
            'allow_social_tool' => 'social',
1122
            'allow_message_tool' => 'message',
1123
            'allow_email_editor' => 'editor',
1124
            'show_link_ticket_notification' => 'display',
1125
            'permissions_for_new_directories' => 'document',
1126
            'enable_profile_user_address_geolocalization' => 'profile',
1127
            'allow_show_skype_account' => 'profile',
1128
            'allow_show_linkedin_url' => 'profile',
1129
            'allow_students_to_create_groups_in_social' => 'social',
1130
            'default_calendar_view' => 'agenda',
1131
            'documents_default_visibility_defined_in_course' => 'document',
1132
            'message_max_upload_filesize' => 'message',
1133
            'course_create_active_tools' => 'course',
1134
            'tool_visible_by_default_at_creation' => 'document',
1135
            'show_users_folders' => 'document',
1136
            'show_default_folders' => 'document',
1137
            'show_chat_folder' => 'chat',
1138
            'enabled_support_svg' => 'editor',
1139
            'enable_webcam_clip' => 'document',
1140
            'permanently_remove_deleted_files' => 'document',
1141
            'allow_delete_attendance' => 'attendance',
1142
            'display_groups_forum_in_general_tool' => 'forum',
1143
            'dropbox_allow_overwrite' => 'dropbox',
1144
            'allow_user_course_subscription_by_course_admin' => 'course',
1145
            'hide_course_group_if_no_tools_available' => 'group',
1146
            'extend_rights_for_coach_on_survey' => 'survey',
1147
            'show_official_code_exercise_result_list' => 'exercise',
1148
            'dropbox_max_filesize' => 'dropbox',
1149
            'dropbox_allow_just_upload' => 'dropbox',
1150
            'dropbox_allow_student_to_student' => 'dropbox',
1151
            'dropbox_allow_group' => 'dropbox',
1152
            'dropbox_allow_mailing' => 'dropbox',
1153
            'upload_extensions_list_type' => 'document',
1154
            'upload_extensions_blacklist' => 'document',
1155
            'upload_extensions_skip' => 'document',
1156
            'changeable_options' => 'profile',
1157
            'users_copy_files' => 'document',
1158
            'document_if_file_exists_option' => 'document',
1159
            'permissions_for_new_files' => 'document',
1160
            'extended_profile' => 'profile',
1161
            'split_users_upload_directory' => 'profile',
1162
            'show_documents_preview' => 'document',
1163
            'messaging_allow_send_push_notification' => 'webservice',
1164
            'messaging_gdc_project_number' => 'webservice',
1165
            'messaging_gdc_api_key' => 'webservice',
1166
            'allow_download_documents_by_api_key' => 'webservice',
1167
            'profiling_filter_adding_users' => 'course',
1168
            'active_tools_on_create' => 'course',
1169
        ];
1170
1171
        return $settings[$variable] ?? $defaultCategory;
1172
    }
1173
1174
    private function transformToString($value): string
1175
    {
1176
        if (\is_array($value)) {
1177
            return implode(',', $value);
1178
        }
1179
1180
        if ($value instanceof Course) {
1181
            return (string) $value->getId();
1182
        }
1183
1184
        if (\is_bool($value)) {
1185
            return $value ? 'true' : 'false';
1186
        }
1187
1188
        if (null === $value) {
1189
            return '';
1190
        }
1191
1192
        return (string) $value;
1193
    }
1194
1195
    private function normalizeNullsBeforeResolve(array $parameters, SettingsBuilder $settingsBuilder): array
1196
    {
1197
        foreach ($parameters as $k => $v) {
1198
            if (null === $v && $settingsBuilder->isDefined($k)) {
1199
                unset($parameters[$k]);
1200
            }
1201
        }
1202
1203
        return $parameters;
1204
    }
1205
}
1206