Issues (2160)

main/inc/lib/plugin.lib.php (8 issues)

1
<?php
2
/* See license terms in /license.txt */
3
4
use ChamiloSession as Session;
5
6
/**
7
 * Class AppPlugin.
8
 */
9
class AppPlugin
10
{
11
    public $plugin_regions = [
12
        'main_top',
13
        'main_bottom',
14
        'login_top',
15
        'login_bottom',
16
        'menu_top',
17
        'menu_bottom',
18
        'content_top',
19
        'content_bottom',
20
        'header_main',
21
        'header_center',
22
        'header_left_logo',
23
        'header_left',
24
        'header_right',
25
        'pre_footer',
26
        'footer_left',
27
        'footer_center',
28
        'footer_right',
29
        'menu_administrator',
30
        'course_tool_plugin',
31
    ];
32
33
    public $installedPluginListName = [];
34
    public $installedPluginListObject = [];
35
    private static $instance;
36
37
    /**
38
     * Constructor.
39
     */
40
    public function __construct()
41
    {
42
    }
43
44
    /**
45
     * @return AppPlugin
46
     */
47
    public static function getInstance()
48
    {
49
        if (!isset(self::$instance)) {
50
            self::$instance = new self();
51
        }
52
53
        return self::$instance;
54
    }
55
56
    /**
57
     * Read plugin from path.
58
     *
59
     * @return array
60
     */
61
    public function read_plugins_from_path()
62
    {
63
        /* We scan the plugin directory. Each folder is a potential plugin. */
64
        $pluginPath = api_get_path(SYS_PLUGIN_PATH);
65
        $plugins = [];
66
        $handle = @opendir($pluginPath);
67
        while (false !== ($file = readdir($handle))) {
68
            if ($file != '.' && $file != '..' && is_dir(api_get_path(SYS_PLUGIN_PATH).$file)) {
69
                $plugins[] = $file;
70
            }
71
        }
72
        @closedir($handle);
0 ignored issues
show
Are you sure the usage of closedir($handle) is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Security Best Practice introduced by
It seems like you do not handle an error condition for closedir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

72
        /** @scrutinizer ignore-unhandled */ @closedir($handle);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
73
        sort($plugins);
74
75
        return $plugins;
76
    }
77
78
    /**
79
     * @return array
80
     */
81
    public function getInstalledPluginListName()
82
    {
83
        if (empty($this->installedPluginListName)) {
84
            $this->installedPluginListName = $this->getInstalledPlugins();
85
        }
86
87
        return $this->installedPluginListName;
88
    }
89
90
    /**
91
     * @return array List of Plugin
92
     */
93
    public function getInstalledPluginListObject()
94
    {
95
        if (empty($this->installedPluginListObject)) {
96
            $this->setInstalledPluginListObject();
97
        }
98
99
        return $this->installedPluginListObject;
100
    }
101
102
    public function setInstalledPluginListObject()
103
    {
104
        $pluginListName = $this->getInstalledPluginListName();
105
        $pluginList = [];
106
        if (!empty($pluginListName)) {
107
            foreach ($pluginListName as $pluginName) {
108
                $pluginInfo = $this->getPluginInfo($pluginName, true);
109
                if (isset($pluginInfo['plugin_class'])) {
110
                    $pluginList[] = $pluginInfo['plugin_class']::create();
111
                }
112
            }
113
        }
114
        $this->installedPluginListObject = $pluginList;
115
    }
116
117
    /**
118
     * @param string $plugin
119
     *
120
     * @return bool
121
     */
122
    public function isInstalled($plugin)
123
    {
124
        $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

124
        /** @scrutinizer ignore-call */ 
125
        $list = self::getInstalledPlugins(false);
Loading history...
125
126
        return in_array($plugin, $list);
127
    }
128
129
    /**
130
     * Returns a list of all installed plugins.
131
     *
132
     * @param bool $fromDatabase
133
     *
134
     * @return array
135
     */
136
    public function getInstalledPlugins($fromDatabase = true)
137
    {
138
        static $installedPlugins = null;
139
140
        if ($fromDatabase === false) {
141
            if (is_array($installedPlugins)) {
142
                return $installedPlugins;
143
            }
144
        }
145
146
        if ($fromDatabase || $installedPlugins === null) {
147
            $installedPlugins = [];
148
149
            /*if (api_is_multiple_url_enabled()) {
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 = ? ' => [
154
                            'status',
155
                            'installed',
156
                            'Plugins',
157
                            $urlId,
158
                        ],
159
                    ]
160
                );
161
            } else {*/
162
            $plugins = api_get_settings_params(
163
                    [
164
                        'variable = ? AND selected_value = ? AND category = ? ' => ['status', 'installed', 'Plugins'],
165
                    ]
166
                );
167
            //}
168
169
            if (!empty($plugins)) {
170
                foreach ($plugins as $row) {
171
                    $installedPlugins[$row['subkey']] = true;
172
                }
173
                $installedPlugins = array_keys($installedPlugins);
174
            }
175
        }
176
177
        return $installedPlugins;
178
    }
179
180
    public function getInstalledPluginsInCurrentUrl()
181
    {
182
        $installedPlugins = [];
183
        $urlId = api_get_current_access_url_id();
184
        $plugins = api_get_settings_params(
185
            [
186
                'variable = ? AND selected_value = ? AND category = ? AND access_url = ?' => ['status', 'installed', 'Plugins', $urlId],
187
            ]
188
        );
189
190
        if (!empty($plugins)) {
191
            foreach ($plugins as $row) {
192
                $installedPlugins[$row['subkey']] = true;
193
            }
194
            $installedPlugins = array_keys($installedPlugins);
195
        }
196
197
        return $installedPlugins;
198
    }
199
200
    /**
201
     * Returns a list of all official (delivered with the Chamilo package)
202
     * plugins. This list is maintained manually and updated with every new
203
     * release to avoid hacking.
204
     *
205
     * @return array
206
     */
207
    public function getOfficialPlugins()
208
    {
209
        static $officialPlugins = null;
210
        // Please keep this list alphabetically sorted
211
        $officialPlugins = [
212
            'add_cas_login_button',
213
            'add_cas_logout_button',
214
            'add_facebook_login_button',
215
            'add_shibboleth_login_button',
216
            'advanced_subscription',
217
            'azure_active_directory',
218
            'bbb',
219
            'before_login',
220
            'buycourses',
221
            'card_game',
222
            'check_extra_field_author_company',
223
            'cleandeletedfiles',
224
            'clockworksms',
225
            'courseblock',
226
            'coursehomenotify',
227
            'courselegal',
228
            'createdrupaluser',
229
            'customcertificate',
230
            'customfooter',
231
            'dashboard',
232
            'date',
233
            'dictionary',
234
            'embedregistry',
235
            'exercise_signature',
236
            'ext_auth_chamilo_logout_button_behaviour',
237
            'externalnotificationconnect',
238
            'extramenufromwebservice',
239
            'follow_buttons',
240
            'formLogin_hide_unhide',
241
            'google_maps',
242
            'google_meet',
243
            'grading_electronic',
244
            'h5p',
245
            'h5pimport',
246
            'hello_world',
247
            'ims_lti',
248
            'justification',
249
            'kannelsms',
250
            'keycloak',
251
            'learning_calendar',
252
            'lti_provider',
253
            'maintenancemode',
254
            'migrationmoodle',
255
            'mindmap',
256
            'nosearchindex',
257
            'notebookteacher',
258
            'oauth2',
259
            'olpc_peru_filter',
260
            'onlyoffice',
261
            'openmeetings',
262
            'pausetraining',
263
            'pens',
264
            'positioning',
265
            'questionoptionsevaluation',
266
            'redirection',
267
            'remedial_course',
268
            'reports',
269
            'resubscription',
270
            'rss',
271
            'search_course',
272
            'send_notification_new_lp',
273
            'sepe',
274
            'share_buttons',
275
            'show_regions',
276
            'show_user_info',
277
            'static',
278
            'studentfollowup',
279
            'surveyexportcsv',
280
            'surveyexporttxt',
281
            'test2pdf',
282
            'toplinks',
283
            'tour',
284
            'userremoteservice',
285
            'vchamilo',
286
            'whispeakauth',
287
            'zoom',
288
            'xapi',
289
            'ai_helper',
290
        ];
291
292
        return $officialPlugins;
293
    }
294
295
    /**
296
     * @param string $pluginName
297
     * @param int    $urlId
298
     */
299
    public function install($pluginName, $urlId = null)
300
    {
301
        $urlId = (int) $urlId;
302
        if (empty($urlId)) {
303
            $urlId = api_get_current_access_url_id();
304
        }
305
306
        api_add_setting(
307
            'installed',
308
            'status',
309
            $pluginName,
310
            'setting',
311
            'Plugins',
312
            $pluginName,
313
            '',
314
            '',
315
            '',
316
            $urlId,
317
            1
318
        );
319
320
        $pluginPath = api_get_path(SYS_PLUGIN_PATH).$pluginName.'/install.php';
321
322
        if (is_file($pluginPath) && is_readable($pluginPath)) {
323
            // Execute the install procedure.
324
325
            require $pluginPath;
326
        }
327
    }
328
329
    /**
330
     * @param string $pluginName
331
     * @param int    $urlId
332
     */
333
    public function uninstall($pluginName, $urlId = null)
334
    {
335
        $urlId = (int) $urlId;
336
        if (empty($urlId)) {
337
            $urlId = api_get_current_access_url_id();
338
        }
339
340
        // First call the custom uninstall to allow full access to global settings
341
        $pluginPath = api_get_path(SYS_PLUGIN_PATH).$pluginName.'/uninstall.php';
342
        if (is_file($pluginPath) && is_readable($pluginPath)) {
343
            // Execute the uninstall procedure.
344
345
            require $pluginPath;
346
        }
347
348
        // Second remove all remaining global settings
349
        api_delete_settings_params(
350
            ['category = ? AND access_url = ? AND subkey = ? ' => ['Plugins', $urlId, $pluginName]]
351
        );
352
    }
353
354
    /**
355
     * @param string $pluginName
356
     *
357
     * @return array
358
     */
359
    public function get_areas_by_plugin($pluginName)
360
    {
361
        $result = api_get_settings('Plugins');
362
        $areas = [];
363
        foreach ($result as $row) {
364
            if ($pluginName == $row['selected_value']) {
365
                $areas[] = $row['variable'];
366
            }
367
        }
368
369
        return $areas;
370
    }
371
372
    /**
373
     * @param string $pluginName
374
     *
375
     * @return bool
376
     */
377
    public function is_valid_plugin($pluginName)
378
    {
379
        if (is_dir(api_get_path(SYS_PLUGIN_PATH).$pluginName)) {
380
            if (is_file(api_get_path(SYS_PLUGIN_PATH).$pluginName.'/index.php')) {
381
                return true;
382
            }
383
        }
384
385
        return false;
386
    }
387
388
    /**
389
     * @return array
390
     */
391
    public function get_plugin_regions()
392
    {
393
        sort($this->plugin_regions);
394
395
        return $this->plugin_regions;
396
    }
397
398
    /**
399
     * @param string   $region
400
     * @param Template $template
401
     * @param bool     $forced
402
     *
403
     * @return string|null
404
     */
405
    public function load_region($region, $template, $forced = false)
406
    {
407
        if ($region == 'course_tool_plugin') {
408
            return '';
409
        }
410
411
        ob_start();
412
        $this->get_all_plugin_contents_by_region($region, $template, $forced);
413
        $content = ob_get_contents();
414
        ob_end_clean();
415
416
        return $content;
417
    }
418
419
    /**
420
     * Loads the translation files inside a plugin if exists.
421
     * It loads by default english see the hello world plugin.
422
     *
423
     * @param string $plugin_name
424
     *
425
     * @todo add caching
426
     */
427
    public function load_plugin_lang_variables($plugin_name)
428
    {
429
        global $language_interface;
430
        $root = api_get_path(SYS_PLUGIN_PATH);
431
        $strings = null;
432
433
        // 1. Loading english if exists
434
        $english_path = $root.$plugin_name."/lang/english.php";
435
436
        if (is_readable($english_path)) {
437
            include $english_path;
438
439
            foreach ($strings as $key => $string) {
0 ignored issues
show
The expression $strings of type null is not traversable.
Loading history...
440
                $GLOBALS[$key] = $string;
441
            }
442
        }
443
444
        // 2. Loading the system language
445
        if ($language_interface != 'english') {
446
            $path = $root.$plugin_name."/lang/$language_interface.php";
447
448
            if (is_readable($path)) {
449
                include $path;
450
                if (!empty($strings)) {
451
                    foreach ($strings as $key => $string) {
452
                        $GLOBALS[$key] = $string;
453
                    }
454
                }
455
            } else {
456
                $interfaceLanguageId = api_get_language_id($language_interface);
457
                $interfaceLanguageInfo = api_get_language_info($interfaceLanguageId);
458
                $languageParentId = intval($interfaceLanguageInfo['parent_id']);
459
460
                if ($languageParentId > 0) {
461
                    $languageParentInfo = api_get_language_info($languageParentId);
462
                    $languageParentFolder = $languageParentInfo['dokeos_folder'];
463
464
                    $parentPath = "{$root}{$plugin_name}/lang/{$languageParentFolder}.php";
465
                    if (is_readable($parentPath)) {
466
                        include $parentPath;
467
                        if (!empty($strings)) {
468
                            foreach ($strings as $key => $string) {
469
                                $this->strings[$key] = $string;
470
                            }
471
                        }
472
                    }
473
                }
474
            }
475
        }
476
    }
477
478
    /**
479
     * @param string   $region
480
     * @param Template $template
481
     * @param bool     $forced
482
     *
483
     * @return bool
484
     *
485
     * @todo improve this function
486
     */
487
    public function get_all_plugin_contents_by_region($region, $template, $forced = false)
488
    {
489
        global $_plugins;
490
        if (isset($_plugins[$region]) && is_array($_plugins[$region])) {
491
            // Load the plugin information
492
            foreach ($_plugins[$region] as $plugin_name) {
493
                // The plugin_info variable is available inside the plugin index
494
                $plugin_info = $this->getPluginInfo($plugin_name, $forced);
495
496
                // We also know where the plugin is
497
                $plugin_info['current_region'] = $region;
498
499
                // Loading the plugin/XXX/index.php file
500
                $plugin_file = api_get_path(SYS_PLUGIN_PATH)."$plugin_name/index.php";
501
502
                if (file_exists($plugin_file)) {
503
                    //Loading the lang variables of the plugin if exists
504
                    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

504
                    self::/** @scrutinizer ignore-call */ 
505
                          load_plugin_lang_variables($plugin_name);
Loading history...
505
506
                    // Printing the plugin index.php file
507
                    require $plugin_file;
508
509
                    // If the variable $_template is set we assign those values to be accessible in Twig
510
                    if (isset($_template)) {
511
                        $_template['plugin_info'] = $plugin_info;
512
                    } else {
513
                        $_template = [];
514
                        $_template['plugin_info'] = $plugin_info;
515
                    }
516
517
                    // Setting the plugin info available in the template if exists.
518
                    $template->assign($plugin_name, $_template);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $_template does not seem to be defined for all execution paths leading up to this point.
Loading history...
519
520
                    // Loading the Twig template plugin files if exists
521
                    $template_list = [];
522
                    if (isset($plugin_info) && isset($plugin_info['templates'])) {
523
                        $template_list = $plugin_info['templates'];
524
                    }
525
526
                    if (!empty($template_list)) {
527
                        foreach ($template_list as $plugin_tpl) {
528
                            if (!empty($plugin_tpl)) {
529
                                $template_plugin_file = "$plugin_name/$plugin_tpl"; // for twig
530
                                $template->display($template_plugin_file, false);
531
                            }
532
                        }
533
                    }
534
                }
535
            }
536
        }
537
538
        return true;
539
    }
540
541
    /**
542
     * Loads plugin info.
543
     *
544
     * @staticvar array $plugin_data
545
     *
546
     * @param string $plugin_name
547
     * @param bool   $forced      load from DB or from the static array
548
     *
549
     * @return array
550
     *
551
     * @todo filter setting_form
552
     */
553
    public function getPluginInfo($plugin_name, $forced = false)
554
    {
555
        $pluginData = Session::read('plugin_data');
556
        if (isset($pluginData[$plugin_name]) && $forced == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
557
            return $pluginData[$plugin_name];
558
        } else {
559
            $plugin_file = api_get_path(SYS_PLUGIN_PATH)."$plugin_name/plugin.php";
560
561
            $plugin_info = [];
562
            if (file_exists($plugin_file)) {
563
                require $plugin_file;
564
            }
565
566
            // @todo check if settings are already added
567
            // Extra options
568
            $plugin_settings = api_get_settings_params(
569
                [
570
                    "subkey = ? AND category = ? AND type = ? AND access_url = ?" => [
571
                        $plugin_name,
572
                        'Plugins',
573
                        'setting',
574
                        api_get_current_access_url_id(),
575
                    ],
576
                ]
577
            );
578
579
            $settings_filtered = [];
580
            foreach ($plugin_settings as $item) {
581
                if (!empty($item['selected_value'])) {
582
                    $unserialized = UnserializeApi::unserialize('not_allowed_classes', $item['selected_value'], true);
583
                    if (false !== $unserialized) {
584
                        $item['selected_value'] = $unserialized;
585
                    }
586
                }
587
                $settings_filtered[$item['variable']] = $item['selected_value'];
588
            }
589
            $plugin_info['settings'] = $settings_filtered;
590
            $pluginData[$plugin_name] = $plugin_info;
591
            Session::write('plugin_data', $pluginData);
592
593
            return $plugin_info;
594
        }
595
    }
596
597
    /**
598
     * Get the template list.
599
     *
600
     * @param string $pluginName
601
     *
602
     * @return bool
603
     */
604
    public function get_templates_list($pluginName)
605
    {
606
        $plugin_info = $this->getPluginInfo($pluginName);
607
        if (isset($plugin_info) && isset($plugin_info['templates'])) {
608
            return $plugin_info['templates'];
609
        }
610
611
        return false;
612
    }
613
614
    /**
615
     * Remove all regions of an specific plugin.
616
     *
617
     * @param string $plugin
618
     */
619
    public function remove_all_regions($plugin)
620
    {
621
        $access_url_id = api_get_current_access_url_id();
622
        if (!empty($plugin)) {
623
            api_delete_settings_params(
624
                [
625
                    'category = ? AND type = ? AND access_url = ? AND subkey = ? ' => [
626
                        'Plugins',
627
                        'region',
628
                        $access_url_id,
629
                        $plugin,
630
                    ],
631
                ]
632
            );
633
        }
634
    }
635
636
    /**
637
     * Add a plugin to a region.
638
     *
639
     * @param string $plugin
640
     * @param string $region
641
     */
642
    public function add_to_region($plugin, $region)
643
    {
644
        api_add_setting(
645
            $plugin,
646
            $region,
647
            $plugin,
648
            'region',
649
            'Plugins',
650
            $plugin,
651
            '',
652
            '',
653
            '',
654
            api_get_current_access_url_id(),
655
            1
656
        );
657
    }
658
659
    /**
660
     * @param int $courseId
661
     */
662
    public function install_course_plugins($courseId)
663
    {
664
        $pluginList = $this->getInstalledPluginListObject();
665
666
        if (!empty($pluginList)) {
667
            /** @var Plugin $obj */
668
            foreach ($pluginList as $obj) {
669
                $pluginName = $obj->get_name();
670
                $plugin_path = api_get_path(SYS_PLUGIN_PATH).$pluginName.'/plugin.php';
671
672
                if (file_exists($plugin_path)) {
673
                    require $plugin_path;
674
                    if (isset($plugin_info) && isset($plugin_info['plugin_class']) && $obj->isCoursePlugin) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $plugin_info does not exist. Did you maybe mean $plugin_path?
Loading history...
675
                        $obj->course_install($courseId);
676
                    }
677
                }
678
            }
679
        }
680
    }
681
682
    /**
683
     * Trigger for Plugin::doWhenDeleting[Item] functions.
684
     *
685
     * @param string $itemType
686
     * @param int    $itemId
687
     */
688
    public function performActionsWhenDeletingItem($itemType, $itemId)
689
    {
690
        $pluginList = $this->getInstalledPluginListObject();
691
692
        if (empty($pluginList)) {
693
            return;
694
        }
695
696
        /** @var Plugin $pluginObj */
697
        foreach ($pluginList as $pluginObj) {
698
            switch ($itemType) {
699
                case 'course':
700
                    $pluginObj->doWhenDeletingCourse($itemId);
701
                    break;
702
                case 'session':
703
                    $pluginObj->doWhenDeletingSession($itemId);
704
                    break;
705
                case 'user':
706
                    $pluginObj->doWhenDeletingUser($itemId);
707
                    break;
708
            }
709
        }
710
    }
711
712
    /**
713
     * Add the course settings to the course settings form.
714
     *
715
     * @param FormValidator $form
716
     */
717
    public function add_course_settings_form($form)
718
    {
719
        $pluginList = $this->getInstalledPluginListObject();
720
        /** @var Plugin $obj */
721
        foreach ($pluginList as $obj) {
722
            $plugin_name = $obj->get_name();
723
            $pluginTitle = $obj->get_title();
724
            if (!empty($obj->course_settings)) {
725
                if (is_file(api_get_path(SYS_CODE_PATH).'img/icons/'.ICON_SIZE_SMALL.'/'.$plugin_name.'.png')) {
726
                    $icon = Display::return_icon(
727
                        $plugin_name.'.png',
728
                        Security::remove_XSS($pluginTitle),
729
                        '',
730
                        ICON_SIZE_SMALL
731
                    );
732
                } else {
733
                    $icon = Display::return_icon(
734
                        'plugins.png',
735
                        Security::remove_XSS($pluginTitle),
736
                        '',
737
                        ICON_SIZE_SMALL
738
                    );
739
                }
740
741
                $form->addHtml('<div class="panel panel-default">');
742
                $form->addHtml('
743
                    <div class="panel-heading" role="tab" id="heading-'.$plugin_name.'-settings">
744
                        <h4 class="panel-title">
745
                            <a class="collapsed"
746
                                role="button" data-toggle="collapse" data-parent="#accordion"
747
                                href="#collapse-'.$plugin_name.'-settings" aria-expanded="false"
748
                                aria-controls="collapse-'.$plugin_name.'-settings">
749
                ');
750
                $form->addHtml($icon.' '.$pluginTitle);
751
                $form->addHtml('
752
                            </a>
753
                        </h4>
754
                    </div>
755
                ');
756
                $form->addHtml('
757
                    <div
758
                        id="collapse-'.$plugin_name.'-settings"
759
                        class="panel-collapse collapse" role="tabpanel"
760
                        aria-labelledby="heading-'.$plugin_name.'-settings">
761
                        <div class="panel-body">
762
                ');
763
764
                $groups = [];
765
                foreach ($obj->course_settings as $setting) {
766
                    if ($obj->validateCourseSetting($setting['name']) === false) {
767
                        continue;
768
                    }
769
                    if ($setting['type'] !== 'checkbox') {
770
                        $form->addElement($setting['type'], $setting['name'], $obj->get_lang($setting['name']));
771
                    } else {
772
                        $element = &$form->createElement(
773
                            $setting['type'],
774
                            $setting['name'],
775
                            '',
776
                            $obj->get_lang($setting['name'])
777
                        );
778
779
                        // Check global settings
780
                        $courseSetting = api_get_course_setting($setting['name']);
781
                        if (-1 === $courseSetting) {
782
                            $defaultValue = api_get_plugin_setting($plugin_name, $setting['name']);
783
                            if (!empty($defaultValue)) {
784
                                if ('true' === $defaultValue) {
785
                                    $element->setChecked(true);
786
                                }
787
                            }
788
                        }
789
790
                        if (isset($setting['init_value']) && $setting['init_value'] == 1) {
791
                            $element->setChecked(true);
792
                        }
793
794
                        $form->addElement($element);
795
                        if (isset($setting['group'])) {
796
                            $groups[$setting['group']][] = $element;
797
                        }
798
                    }
799
                }
800
                foreach ($groups as $k => $v) {
801
                    $form->addGroup($groups[$k], $k, [$obj->get_lang($k)]);
802
                }
803
                $form->addButtonSave(get_lang('SaveSettings'));
804
                $form->addHtml('
805
                        </div>
806
                    </div>
807
                ');
808
                $form->addHtml('</div>');
809
            }
810
        }
811
    }
812
813
    /**
814
     * Get all course settings from all installed plugins.
815
     *
816
     * @return array
817
     */
818
    public function getAllPluginCourseSettings()
819
    {
820
        $pluginList = $this->getInstalledPluginListObject();
821
        /** @var Plugin $obj */
822
        $courseSettings = [];
823
        if (!empty($pluginList)) {
824
            foreach ($pluginList as $obj) {
825
                $pluginCourseSetting = $obj->getCourseSettings();
826
                $courseSettings = array_merge($courseSettings, $pluginCourseSetting);
827
            }
828
        }
829
830
        return $courseSettings;
831
    }
832
833
    /**
834
     * When saving the plugin values in the course settings, check whether
835
     * a callback method should be called and send it the updated settings.
836
     *
837
     * @param array $values The new settings the user just saved
838
     */
839
    public function saveCourseSettingsHook($values)
840
    {
841
        $pluginList = $this->getInstalledPluginListObject();
842
843
        /** @var Plugin $obj */
844
        foreach ($pluginList as $obj) {
845
            $settings = $obj->getCourseSettings();
846
            $subValues = [];
847
            if (!empty($settings)) {
848
                foreach ($settings as $v) {
849
                    if (isset($values[$v])) {
850
                        $subValues[$v] = $values[$v];
851
                    }
852
                }
853
            }
854
855
            if (!empty($subValues)) {
856
                $obj->course_settings_updated($subValues);
857
            }
858
        }
859
    }
860
861
    /**
862
     * Get first SMS plugin name.
863
     *
864
     * @return string|bool
865
     */
866
    public function getSMSPluginName()
867
    {
868
        $installedPluginsList = $this->getInstalledPluginListObject();
869
        foreach ($installedPluginsList as $installedPlugin) {
870
            if ($installedPlugin->isMailPlugin) {
871
                return get_class($installedPlugin);
872
            }
873
        }
874
875
        return false;
876
    }
877
878
    /**
879
     * @return SmsPluginLibraryInterface
880
     */
881
    public function getSMSPluginLibrary()
882
    {
883
        $className = $this->getSMSPluginName();
884
        $className = str_replace('Plugin', '', $className);
885
886
        if (class_exists($className)) {
887
            return new $className();
888
        }
889
890
        return false;
891
    }
892
893
    public static function cleanEntitiesInBundle()
894
    {
895
        $pluginList = [
896
            'CourseHomeNotify',
897
            'EmbedRegistry',
898
            'ImsLti',
899
            'LtiProvider',
900
            'StudentFollowUp',
901
            'WhispeakAuth',
902
        ];
903
904
        foreach ($pluginList as $pluginName) {
905
            $entityPath = api_get_path(SYS_PATH).'src/Chamilo/PluginBundle/Entity/'.$pluginName;
906
907
            if (!is_dir($entityPath)) {
908
                continue;
909
            }
910
911
            if (!is_writable($entityPath)) {
912
                continue;
913
            }
914
915
            rmdirr($entityPath);
916
        }
917
    }
918
}
919