Passed
Push — master ( 894ff6...5a2620 )
by Angel Fernando Quiroz
08:41
created

AppPlugin::removeAllRegions()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 8
nc 2
nop 1
dl 0
loc 11
rs 10
c 0
b 0
f 0
1
<?php
2
/* See license terms in /license.txt */
3
4
use Chamilo\CoreBundle\Enums\ToolIcon;
5
use Chamilo\CoreBundle\Framework\Container;
6
use Symfony\Component\Finder\Finder;
7
8
/**
9
 * Class AppPlugin.
10
 */
11
class AppPlugin
12
{
13
    public $plugin_regions = [
14
        'main_top',
15
        'main_bottom',
16
        'login_top',
17
        'login_bottom',
18
        'menu_top',
19
        'menu_bottom',
20
        'content_top',
21
        'content_bottom',
22
        'header_main',
23
        'header_center',
24
        'header_left',
25
        'header_right',
26
        'pre_footer',
27
        'footer_left',
28
        'footer_center',
29
        'footer_right',
30
        'menu_administrator',
31
        'course_tool_plugin',
32
    ];
33
34
    public $installedPluginListName = [];
35
    public $installedPluginListObject = [];
36
    private static $instance;
37
38
    /**
39
     * Constructor.
40
     */
41
    public function __construct()
42
    {
43
    }
44
45
    /**
46
     * @return AppPlugin
47
     */
48
    public static function getInstance()
49
    {
50
        if (!isset(self::$instance)) {
51
            self::$instance = new self();
52
        }
53
54
        return self::$instance;
55
    }
56
57
    /**
58
     * Read plugin from path.
59
     */
60
    public function read_plugins_from_path(): array
61
    {
62
        /* We scan the plugin directory. Each folder is a potential plugin. */
63
        $pluginPath = api_get_path(SYS_PLUGIN_PATH);
64
        $finder = (new Finder())->directories()->depth('== 0')->sortByName()->in($pluginPath);
65
66
        $plugins = [];
67
68
        foreach ($finder as $file) {
69
            $plugins[] = $file->getFilename();
70
        }
71
72
        return $plugins;
73
    }
74
75
    /**
76
     * @return array
77
     */
78
    public function getInstalledPluginListName()
79
    {
80
        if (empty($this->installedPluginListName)) {
81
            $this->installedPluginListName = $this->getInstalledPlugins();
82
        }
83
84
        return $this->installedPluginListName;
85
    }
86
87
    /**
88
     * @return array List of Plugin
89
     */
90
    public function getInstalledPluginListObject()
91
    {
92
        if (empty($this->installedPluginListObject)) {
93
            $this->setInstalledPluginListObject();
94
        }
95
96
        return $this->installedPluginListObject;
97
    }
98
99
    public function setInstalledPluginListObject()
100
    {
101
        $pluginListName = $this->getInstalledPluginListName();
102
        $pluginList = [];
103
        if (!empty($pluginListName)) {
104
            foreach ($pluginListName as $pluginName) {
105
                $pluginInfo = $this->getPluginInfo($pluginName, true);
106
                if (isset($pluginInfo['plugin_class'])) {
107
                    $pluginList[] = $pluginInfo['plugin_class']::create();
108
                }
109
            }
110
        }
111
        $this->installedPluginListObject = $pluginList;
112
    }
113
114
    /**
115
     * @param string $plugin
116
     *
117
     * @return bool
118
     */
119
    public function isInstalled($plugin)
120
    {
121
        $list = self::getInstalledPlugins(false);
0 ignored issues
show
Bug Best Practice introduced by
The method AppPlugin::getInstalledPlugins() is not static, but was called statically. ( Ignorable by Annotation )

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

121
        /** @scrutinizer ignore-call */ 
122
        $list = self::getInstalledPlugins(false);
Loading history...
122
123
        return in_array($plugin, $list);
124
    }
125
126
    public function getInstalledPlugins(bool $fromDatabase = true): array
127
    {
128
        static $installedPlugins = null;
129
130
        if (false === $fromDatabase && is_array($installedPlugins)) {
131
            return $installedPlugins;
132
        }
133
134
        if ($fromDatabase || null === $installedPlugins) {
135
            $installedPlugins = [];
136
137
            $plugins = Container::getPluginRepository()->getInstalledPlugins();
138
139
            foreach ($plugins as $plugin) {
140
                $installedPlugins[] = $plugin->getTitle();
141
            }
142
        }
143
144
        return $installedPlugins;
145
    }
146
147
    public function getInstalledPluginsInCurrentUrl()
148
    {
149
        $installedPlugins = [];
150
        $urlId = api_get_current_access_url_id();
151
        $plugins = api_get_settings_params(
152
            [
153
                'variable = ? AND selected_value = ? AND category = ? AND access_url = ?' => ['status', 'installed', 'Plugins', $urlId],
154
            ]
155
        );
156
157
        if (!empty($plugins)) {
158
            foreach ($plugins as $row) {
159
                $installedPlugins[$row['subkey']] = true;
160
            }
161
            $installedPlugins = array_keys($installedPlugins);
162
        }
163
164
        return $installedPlugins;
165
    }
166
167
    /**
168
     * Returns a list of all official (delivered with the Chamilo package)
169
     * plugins. This list is maintained manually and updated with every new
170
     * release to avoid hacking.
171
     *
172
     * @return array
173
     */
174
    public static function getOfficialPlugins(): array
175
    {
176
        // Please keep this list alphabetically sorted
177
        return [
178
            'AzureActiveDirectory',
179
            'Bbb',
180
            'BeforeLogin',
181
            'BuyCourses',
182
            'CardGame',
183
            'CheckExtraFieldAuthorCompany',
184
            'CleanDeletedFiles',
185
            'CourseBlock',
186
            'CourseHomeNotify',
187
            'CourseLegal',
188
            'CustomCertificate',
189
            'CustomFooter',
190
            'Dashboard',
191
            'Dictionary',
192
            'EmbedRegistry',
193
            'ExerciseSignature',
194
            'ExtAuthChamiloLogoutButtonBehaviour',
195
            'ExternalNotificationConnect',
196
            'ExtraMenuFromWebservice',
197
            'GoogleMaps',
198
            'GradingElectronic',
199
            'H5pImport',
200
            'HelloWorld',
201
            'ImsLti',
202
            'Justification',
203
            'LearningCalendar',
204
            'LtiProvider',
205
            'MaintenanceMode',
206
            'MigrationMoodle',
207
            'Mobidico',
208
            'NoSearchIndex',
209
            'NotebookTeacher',
210
            'PauseTraining',
211
            'Pens',
212
            'Positioning',
213
            'QuestionOptionsEvaluation',
214
            'Redirection',
215
            'Resubscription',
216
            'Rss',
217
            'SearchCourse',
218
            'ShowRegions',
219
            'ShowUserInfo',
220
            'Static',
221
            'StudentFollowUp',
222
            'SurveyExportCsv',
223
            'SurveyExportTxt',
224
            'Test2Pdf',
225
            'TopLinks',
226
            'Tour',
227
            'UserRemoteService',
228
            'XApi',
229
            'Zoom',
230
        ];
231
    }
232
233
    public static function isOfficial(string $title): bool
234
    {
235
        return in_array($title, self::getOfficialPlugins());
236
    }
237
238
    public function install(string $pluginName): void
239
    {
240
        $pluginPath = api_get_path(SYS_PLUGIN_PATH).$pluginName.'/install.php';
241
242
        if (is_file($pluginPath) && is_readable($pluginPath)) {
243
            // Execute the install procedure.
244
245
            require $pluginPath;
246
        }
247
    }
248
249
    public function uninstall(string $pluginName): void
250
    {
251
        // First call the custom uninstallation to allow full access to global settings
252
        $pluginPath = api_get_path(SYS_PLUGIN_PATH).$pluginName.'/uninstall.php';
253
        if (is_file($pluginPath) && is_readable($pluginPath)) {
254
            // Execute the uninstall procedure.
255
256
            require $pluginPath;
257
        }
258
    }
259
260
    /**
261
     * @param string $pluginName
262
     *
263
     * @return array
264
     */
265
    public function get_areas_by_plugin($pluginName)
266
    {
267
        $result = api_get_settings('Plugins');
268
        $areas = [];
269
        foreach ($result as $row) {
270
            if ($pluginName == $row['selected_value']) {
271
                $areas[] = $row['variable'];
272
            }
273
        }
274
275
        return $areas;
276
    }
277
278
    /**
279
     * @param string $pluginName
280
     *
281
     * @return bool
282
     */
283
    public function is_valid_plugin($pluginName)
284
    {
285
        if (is_dir(api_get_path(SYS_PLUGIN_PATH).$pluginName)) {
286
            if (is_file(api_get_path(SYS_PLUGIN_PATH).$pluginName.'/index.php')) {
287
                return true;
288
            }
289
        }
290
291
        return false;
292
    }
293
294
    /**
295
     * @return array
296
     */
297
    public function getPluginRegions()
298
    {
299
        sort($this->plugin_regions);
300
301
        return $this->plugin_regions;
302
    }
303
304
    /**
305
     * @param string           $region
306
     * @param Twig_Environment $template
307
     * @param bool             $forced
308
     *
309
     * @return string|null
310
     */
311
    public function loadRegion($pluginName, $region, $template, $forced = false)
312
    {
313
        if ('course_tool_plugin' == $region) {
314
            return '';
315
        }
316
317
        ob_start();
318
        $this->getAllPluginContentsByRegion($pluginName, $region, $template, $forced);
319
        $content = ob_get_contents();
320
        ob_end_clean();
321
322
        return $content;
323
    }
324
325
    /**
326
     * Loads the translation files inside a plugin if exists.
327
     * It loads by default english see the hello world plugin.
328
     *
329
     * @param string $plugin_name
330
     *
331
     * @todo add caching
332
     */
333
    public function load_plugin_lang_variables($plugin_name)
334
    {
335
        $language_interface = api_get_language_isocode();
336
        $root = api_get_path(SYS_PLUGIN_PATH);
337
        $strings = null;
338
339
        // 1. Loading english if exists
340
        $english_path = $root.$plugin_name.'/lang/english.php';
341
        if (is_readable($english_path)) {
342
            include $english_path;
343
344
            foreach ($strings as $key => $string) {
0 ignored issues
show
Bug introduced by
The expression $strings of type null is not traversable.
Loading history...
345
                $GLOBALS[$key] = $string;
346
            }
347
        }
348
349
        // 2. Loading the system language
350
        if ('english' != $language_interface) {
351
            $path = $root.$plugin_name."/lang/$language_interface.php";
352
            if (is_readable($path)) {
353
                include $path;
354
                if (!empty($strings)) {
355
                    foreach ($strings as $key => $string) {
356
                        $GLOBALS[$key] = $string;
357
                    }
358
                }
359
            } else {
360
                /*$interfaceLanguageId = api_get_language_id($language_interface);
361
                $interfaceLanguageInfo = api_get_language_info($interfaceLanguageId);
362
                $languageParentId = intval($interfaceLanguageInfo['parent_id']);
363
364
                if ($languageParentId > 0) {
365
                    $languageParentInfo = api_get_language_info($languageParentId);
366
                    $languageParentFolder = $languageParentInfo['dokeos_folder'];
367
368
                    $parentPath = "{$root}{$plugin_name}/lang/{$languageParentFolder}.php";
369
                    if (is_readable($parentPath)) {
370
                        include $parentPath;
371
                        if (!empty($strings)) {
372
                            foreach ($strings as $key => $string) {
373
                                $this->strings[$key] = $string;
374
                            }
375
                        }
376
                    }
377
                }*/
378
            }
379
        }
380
    }
381
382
    /**
383
     * @param string           $region
384
     * @param Twig_Environment $template
385
     * @param bool             $forced
386
     *
387
     * @return bool
388
     *
389
     * @todo improve this function
390
     */
391
    public function getAllPluginContentsByRegion($plugin_name, $region, $template, $forced = false)
392
    {
393
        // The plugin_info variable is available inside the plugin index
394
        $plugin_info = $this->getPluginInfo($plugin_name, $forced);
395
396
        // We also know where the plugin is
397
        $plugin_info['current_region'] = $region;
398
399
        // Loading the plugin/XXX/index.php file
400
        $plugin_file = api_get_path(SYS_PLUGIN_PATH)."$plugin_name/index.php";
401
402
        if (file_exists($plugin_file)) {
403
            //Loading the lang variables of the plugin if exists
404
            self::load_plugin_lang_variables($plugin_name);
0 ignored issues
show
Bug Best Practice introduced by
The method AppPlugin::load_plugin_lang_variables() is not static, but was called statically. ( Ignorable by Annotation )

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

404
            self::/** @scrutinizer ignore-call */ 
405
                  load_plugin_lang_variables($plugin_name);
Loading history...
405
406
            // Printing the plugin index.php file
407
            require $plugin_file;
408
409
            // If the variable $_template is set we assign those values to be accessible in Twig
410
            if (isset($_template)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $_template seems to never exist and therefore isset should always be false.
Loading history...
411
                $_template['plugin_info'] = $plugin_info;
412
            } else {
413
                $_template = [];
414
                $_template['plugin_info'] = $plugin_info;
415
            }
416
417
            // Setting the plugin info available in the template if exists.
418
            //$template->addGlobal($plugin_name, $_template);
419
420
            // Loading the Twig template plugin files if exists
421
            $templateList = [];
422
            if (isset($plugin_info) && isset($plugin_info['templates'])) {
423
                $templateList = $plugin_info['templates'];
424
            }
425
426
            if (!empty($templateList)) {
427
                foreach ($templateList as $pluginTemplate) {
428
                    if (!empty($pluginTemplate)) {
429
                        $templatePluginFile = "$plugin_name/$pluginTemplate"; // for twig
430
                        //$template->render($templatePluginFile, []);
431
                    }
432
                }
433
            }
434
        }
435
436
        return true;
437
    }
438
439
    /**
440
     * Loads plugin info.
441
     *
442
     * @staticvar array $plugin_data
443
     *
444
     * @param string $pluginName
445
     * @param bool   $forced     load from DB or from the static array
446
     *
447
     * @return array
448
     *
449
     * @todo filter setting_form
450
     */
451
    public function getPluginInfo($pluginName, $forced = false)
452
    {
453
        $plugin_info = [];
454
        $pluginPath  = api_get_path(SYS_PLUGIN_PATH);
455
456
        $pluginDir = null;
457
        foreach ([$pluginName, strtolower($pluginName), ucfirst(strtolower($pluginName))] as $dir) {
458
            $path = $pluginPath . "$dir/plugin.php";
459
            if (is_file($path)) {
460
                $fileToLoad = true;
461
                include_once $path;
462
                $pluginDir = $dir;
463
                break;
464
            }
465
        }
466
467
        if (isset($plugin_info['plugin_class']) && class_exists($plugin_info['plugin_class'], false)) {
468
            $cls = $plugin_info['plugin_class'];
469
            $instance = method_exists($cls, 'create') ? $cls::create() : new $cls();
470
            if (method_exists($instance, 'get_info')) {
471
                $plugin_info = $instance->get_info();
472
            }
473
        }
474
475
        $repo   = Container::getPluginRepository();
476
        $entity = $repo->findOneByTitle($pluginName) ?: $repo->findOneByTitle(ucfirst(strtolower($pluginName)));
477
        if ($entity) {
478
            $configByUrl = $entity->getConfigurationsByAccessUrl(Container::getAccessUrlUtil()->getCurrent());
479
            $plugin_info['settings'] = $configByUrl?->getConfiguration() ?? [];
480
        }
481
482
        return $plugin_info;
483
    }
484
485
    /**
486
     * Get the template list.
487
     *
488
     * @param string $pluginName
489
     *
490
     * @return bool
491
     */
492
    public function get_templates_list($pluginName)
493
    {
494
        $plugin_info = $this->getPluginInfo($pluginName);
495
        if (isset($plugin_info) && isset($plugin_info['templates'])) {
496
            return $plugin_info['templates'];
497
        }
498
499
        return false;
500
    }
501
502
    /**
503
     * @param int $courseId
504
     */
505
    public function install_course_plugins(int $courseId): void
506
    {
507
        $pluginList = $this->getInstalledPluginListObject();
508
        if (empty($pluginList)) {
509
            return;
510
        }
511
512
        $accessUrl = Container::getAccessUrlUtil()->getCurrent();
513
        $pluginRepo = Container::getPluginRepository();
514
515
        /** @var Plugin $obj */
516
        foreach ($pluginList as $obj) {
517
            if (empty($obj->isCoursePlugin)) {
518
                continue;
519
            }
520
521
            $entity = $pluginRepo->findOneByTitle($obj->get_name());
522
            $rel    = $entity?->getConfigurationsByAccessUrl($accessUrl);
523
            if (!$rel || !$rel->isActive()) {
524
                continue;
525
            }
526
527
            $obj->get_settings(true);
528
529
            $obj->course_install($courseId);
530
        }
531
    }
532
533
    /**
534
     * Trigger for Plugin::doWhenDeleting[Item] functions.
535
     *
536
     * @param string $itemType
537
     * @param int    $itemId
538
     */
539
    public function performActionsWhenDeletingItem($itemType, $itemId)
540
    {
541
        $pluginList = $this->getInstalledPluginListObject();
542
543
        if (empty($pluginList)) {
544
            return;
545
        }
546
547
        /** @var Plugin $pluginObj */
548
        foreach ($pluginList as $pluginObj) {
549
            switch ($itemType) {
550
                case 'course':
551
                    $pluginObj->doWhenDeletingCourse($itemId);
552
                    break;
553
                case 'session':
554
                    $pluginObj->doWhenDeletingSession($itemId);
555
                    break;
556
                case 'user':
557
                    $pluginObj->doWhenDeletingUser($itemId);
558
                    break;
559
            }
560
        }
561
    }
562
563
    /**
564
     * Add the course settings to the course settings form.
565
     *
566
     * @param FormValidator $form
567
     */
568
    public function add_course_settings_form($form)
569
    {
570
        $pluginList = $this->getInstalledPluginListObject();
571
        /** @var Plugin $obj */
572
        foreach ($pluginList as $obj) {
573
            $pluginName = $obj->get_name();
574
            $pluginTitle = $obj->get_title();
575
            if (!empty($obj->course_settings)) {
576
                $icon = Display::getMdiIcon(
577
                    ToolIcon::PLUGIN,
578
                    'ch-tool-icon',
579
                    null,
580
                    ICON_SIZE_SMALL,
581
                    Security::remove_XSS($pluginTitle)
582
                );
583
                $form->addHtml('<div class="panel panel-default">');
584
                $form->addHtml('
585
                    <div class="panel-heading" role="tab" id="heading-'.$pluginName.'-settings">
586
                        <h4 class="panel-title">
587
                            <a class="collapsed"
588
                                role="button" data-toggle="collapse" data-parent="#accordion"
589
                                href="#collapse-'.$pluginName.'-settings" aria-expanded="false"
590
                                aria-controls="collapse-'.$pluginName.'-settings">
591
                ');
592
                $form->addHtml($icon.' '.$pluginTitle);
593
                $form->addHtml('
594
                            </a>
595
                        </h4>
596
                    </div>
597
                ');
598
                $form->addHtml('
599
                    <div
600
                        id="collapse-'.$pluginName.'-settings"
601
                        class="panel-collapse collapse" role="tabpanel"
602
                        aria-labelledby="heading-'.$pluginName.'-settings">
603
                        <div class="panel-body">
604
                '
605
                );
606
607
                $groups = [];
608
                foreach ($obj->course_settings as $setting) {
609
                    if (false === $obj->validateCourseSetting($setting['name'])) {
610
                        continue;
611
                    }
612
                    if ('checkbox' !== $setting['type']) {
613
                        $form->addElement($setting['type'], $setting['name'], $obj->get_lang($setting['name']));
614
                    } else {
615
                        $element = &$form->createElement(
616
                            $setting['type'],
617
                            $setting['name'],
618
                            '',
619
                            $obj->get_lang($setting['name'])
620
                        );
621
                        $courseSetting = api_get_course_setting($setting['name']);
622
                        if (-1 === $courseSetting) {
623
                            $defaultValue = api_get_plugin_setting($pluginName, $setting['name']);
624
                            if (!empty($defaultValue)) {
625
                                if ('true' === $defaultValue) {
626
                                    $element->setChecked(true);
627
                                }
628
                            }
629
                        }
630
631
                        if (isset($setting['init_value']) && 1 == $setting['init_value']) {
632
                            $element->setChecked(true);
633
                        }
634
                        $form->addElement($element);
635
636
                        if (isset($setting['group'])) {
637
                            $groups[$setting['group']][] = $element;
638
                        }
639
                    }
640
                }
641
                foreach ($groups as $k => $v) {
642
                    $form->addGroup($groups[$k], $k, [$obj->get_lang($k)]);
643
                }
644
                $form->addButtonSave(get_lang('Save settings'));
645
                $form->addHtml(
646
            '
647
                        </div>
648
                    </div>
649
                '
650
        );
651
                $form->addHtml('</div>');
652
            }
653
        }
654
    }
655
656
    /**
657
     * Get all course settings from all installed plugins.
658
     *
659
     * @return array
660
     */
661
    public function getAllPluginCourseSettings()
662
    {
663
        $pluginList = $this->getInstalledPluginListObject();
664
        /** @var Plugin $obj */
665
        $courseSettings = [];
666
        if (!empty($pluginList)) {
667
            foreach ($pluginList as $obj) {
668
                $pluginCourseSetting = $obj->getCourseSettings();
669
                $courseSettings = array_merge($courseSettings, $pluginCourseSetting);
670
            }
671
        }
672
673
        return $courseSettings;
674
    }
675
676
    /**
677
     * When saving the plugin values in the course settings, check whether
678
     * a callback method should be called and send it the updated settings.
679
     *
680
     * @param array $values The new settings the user just saved
681
     */
682
    public function saveCourseSettingsHook($values)
683
    {
684
        $pluginList = $this->getInstalledPluginListObject();
685
686
        /** @var Plugin $obj */
687
        foreach ($pluginList as $obj) {
688
            $settings = $obj->getCourseSettings();
689
            $subValues = [];
690
            if (!empty($settings)) {
691
                foreach ($settings as $v) {
692
                    if (isset($values[$v])) {
693
                        $subValues[$v] = $values[$v];
694
                    }
695
                }
696
            }
697
698
            if (!empty($subValues)) {
699
                $obj->course_settings_updated($subValues);
700
            }
701
        }
702
    }
703
704
    /**
705
     * @param array            $pluginRegionList
706
     * @param string           $pluginRegion
707
     * @param Twig_Environment $twig
708
     */
709
    public function setPluginRegion($pluginRegionList, $pluginRegion, $twig)
710
    {
711
        $regionContent = $this->loadRegion(
712
            $pluginRegionList,
713
            $pluginRegion,
714
            $twig,
715
            true //$this->force_plugin_load
716
        );
717
718
        //$twig->addGlobal('plugin_'.$pluginRegion, $regionContent);
719
    }
720
}
721