Passed
Push — master ( 888f66...5c6945 )
by Angel Fernando Quiroz
08:42 queued 14s
created

AppPlugin::getInstalledPlugins()   A

Complexity

Conditions 6
Paths 3

Size

Total Lines 19
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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