Issues (2037)

plugin/vchamilo/lib/Virtual.php (15 issues)

1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Cocur\Slugify\Slugify;
5
use Doctrine\ORM\EntityManager;
6
use Symfony\Component\Console\Input\ArrayInput;
7
8
/**
9
 * Class Virtual.
10
 */
11
class Virtual
12
{
13
    /**
14
     * @param array $_configuration
15
     */
16
    public static function hookConfiguration(&$_configuration)
17
    {
18
        global $virtualChamilo;
19
20
        if (defined('CLI_SCRIPT') && !defined('CLI_VCHAMILO_OVERRIDE')) {
21
            return;
22
        }
23
24
        // provides an effective value for the virtual root_web based on domain analysis
25
        self::getHostName($_configuration);
26
27
        // We are on physical chamilo. Let original config play
28
        $virtualChamiloWebRoot = rtrim($_configuration['vchamilo_web_root'], '/').'/';
29
30
        $virtualChamilo = [];
31
        if ($_configuration['root_web'] == $virtualChamiloWebRoot) {
32
            return;
33
        }
34
35
        // pre hook to chamilo main table and get alternate configuration.
36
        // sure Database object is not set up. Soo use bootstrap connection
37
        /** @var \Doctrine\DBAL\Connection $connection */
38
        $connection = self::bootConnection($_configuration);
39
40
        $query = "SELECT * FROM vchamilo WHERE root_web = '$virtualChamiloWebRoot'";
41
        $result = $connection->executeQuery($query);
42
43
        if ($result->rowCount()) {
44
            $data = $result->fetch();
45
            $excludes = ['id', 'name'];
46
            $query = "SELECT * FROM settings_current WHERE subkey = 'vchamilo'";
47
            $virtualSettings = $connection->executeQuery($query);
48
            $virtualSettings = $virtualSettings->fetchAll();
49
50
            $homePath = '';
51
            $coursePath = '';
52
            $archivePath = '';
53
            $uploadPath = '';
54
            $passwordEncryption = '';
55
            foreach ($virtualSettings as $setting) {
56
                switch ($setting['variable']) {
57
                    case 'vchamilo_upload_real_root':
58
                        $uploadPath = $setting['selected_value'];
59
                        break;
60
                    case 'vchamilo_home_real_root':
61
                        $homePath = $setting['selected_value'];
62
                        break;
63
                    case 'vchamilo_course_real_root':
64
                        $coursePath = $setting['selected_value'];
65
                        break;
66
                    case 'vchamilo_archive_real_root':
67
                        $archivePath = $setting['selected_value'];
68
                        break;
69
                    case 'vchamilo_password_encryption':
70
                        $passwordEncryption = $setting['selected_value'];
71
                        break;
72
                }
73
            }
74
75
            if (empty($homePath) || empty($coursePath) || empty($archivePath) || empty($uploadPath)) {
76
                echo 'Configure correctly the vchamilo plugin';
77
                exit;
78
            }
79
80
            // Only load if is visible
81
            if ($data && $data['visible'] === '1') {
82
                foreach ($data as $key => $value) {
83
                    if (!in_array($key, $excludes)) {
84
                        // Avoid empty password_encryption
85
                        if ($key == 'password_encryption' && empty($value)) {
86
                            continue;
87
                        }
88
                        $_configuration[$key] = $value;
89
                    }
90
                    $_configuration['virtual'] = $data['root_web'].'/';
91
                }
92
93
                $data['SYS_ARCHIVE_PATH'] = self::addTrailingSlash($archivePath).$data['slug'];
94
                $data['SYS_HOME_PATH'] = self::addTrailingSlash($homePath).$data['slug'];
95
                $data['SYS_COURSE_PATH'] = self::addTrailingSlash($coursePath).$data['slug'];
96
                $data['SYS_UPLOAD_PATH'] = self::addTrailingSlash($uploadPath).$data['slug'];
97
98
                $data['WEB_HOME_PATH'] = self::addTrailingSlash($data['home_url']);
99
                $data['WEB_UPLOAD_PATH'] = self::addTrailingSlash($data['upload_url']);
100
                $data['WEB_ARCHIVE_PATH'] = self::addTrailingSlash($data['archive_url']);
101
102
                if (!empty($passwordEncryption)) {
103
                    $_configuration['password_encryption'] = $passwordEncryption;
104
                }
105
106
                // Instance cannot have multiple urls
107
                $_configuration['multiple_access_urls'] = false;
108
                $_configuration['virtual_css_theme_folder'] = '';
109
                if (isset($data['css_theme_folder']) && !empty($data['css_theme_folder'])) {
110
                    $_configuration['virtual_css_theme_folder'] = $data['css_theme_folder'];
111
                }
112
                $virtualChamilo = $data;
113
            } else {
114
                exit("This portal is disabled. Please contact your administrator");
115
            }
116
        } // otherwise it means the plugin was not configured yet
117
    }
118
119
    /**
120
     * @param array $_configuration
121
     */
122
    public static function getHostName(&$_configuration)
123
    {
124
        if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
125
            $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' || !empty($_configuration['force_https_forwarded_proto'])
126
        ) {
127
            $protocol = 'https';
128
        } else {
129
            if (!empty($_SERVER['HTTPS'])) {
130
                $protocol = 'https';
131
            } else {
132
                $protocol = 'http';
133
            }
134
        }
135
136
        if (defined('CLI_VCHAMILO_OVERRIDE')) {
137
            $_configuration['vchamilo_web_root'] = CLI_VCHAMILO_OVERRIDE;
0 ignored issues
show
The constant CLI_VCHAMILO_OVERRIDE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
138
            $_configuration['vchamilo_name'] = preg_replace('#https?://#', '', CLI_VCHAMILO_OVERRIDE);
139
            // remove radical from override for name
140
141
            // fake the server signature
142
            global $_SERVER;
143
144
            $_SERVER['SERVER_NAME'] = $_configuration['vchamilo_name'];
145
            $_SERVER['HTTP_HOST'] = $_configuration['vchamilo_name'];
146
            $_SERVER['QUERY_STRING'] = '';
147
            $_SERVER['REQUEST_URI'] = CLI_VCHAMILO_OVERRIDE;
148
149
            return;
150
        }
151
152
        $contentPrefix = '/';
153
        if (isset($_SERVER['CONTEXT_PREFIX']) && !empty($_SERVER['CONTEXT_PREFIX'])) {
154
            $contentPrefix = $_SERVER['CONTEXT_PREFIX'];
155
        } else {
156
            // Getting url_append from URL
157
            if (isset($_SERVER['REQUEST_URI'])) {
158
                $requestUri = $_SERVER['REQUEST_URI'];
159
                if (strpos($requestUri, '/courses/') !== false) {
160
                    $result = substr($requestUri, 0, strpos($requestUri, '/courses/'));
161
                    if (!empty($result) && $result != '/') {
162
                        $contentPrefix = $result;
163
                    }
164
                }
165
            }
166
        }
167
168
        $_configuration['vchamilo_web_root'] = "{$protocol}://".@$_SERVER['HTTP_HOST'].$contentPrefix;
169
170
        $_configuration['vchamilo_name'] = @$_SERVER['HTTP_HOST'];
171
        if (empty($_configuration['vchamilo_name'])) { // try again with another source if has failed
172
            $_configuration['vchamilo_name'] = "{$protocol}://".$_SERVER['SERVER_NAME'];
173
            if ($_SERVER['SERVER_PORT'] != 80) {
174
                $_configuration['vchamilo_name'] .= ':'.$_SERVER['SERVER_PORT'];
175
            }
176
            $_configuration['vchamilo_name'] = $_SERVER['SERVER_NAME'];
177
        }
178
    }
179
180
    /**
181
     * @param string $path
182
     *
183
     * @return string
184
     */
185
    public static function addTrailingSlash($path)
186
    {
187
        return substr($path, -1) == '/' ? $path : $path.'/';
188
    }
189
190
    /**
191
     * provides a side connection to a vchamilo database.
192
     *
193
     * @param array $_configuration
194
     *
195
     * @return \Doctrine\DBAL\Driver\Connection
196
     */
197
    public static function bootConnection(&$_configuration)
198
    {
199
        $dbParams = [
200
            'driver' => 'pdo_mysql',
201
            'host' => $_configuration['db_host'],
202
            'user' => $_configuration['db_user'],
203
            'password' => $_configuration['db_password'],
204
            'dbname' => isset($_configuration['main_database']) ? $_configuration['main_database'] : '',
205
            // Only relevant for pdo_sqlite, specifies the path to the SQLite database.
206
            'path' => isset($_configuration['db_path']) ? $_configuration['db_path'] : '',
207
            // Only relevant for pdo_mysql, pdo_pgsql, and pdo_oci/oci8,
208
            'port' => isset($_configuration['db_port']) ? $_configuration['db_port'] : '',
209
        ];
210
211
        try {
212
            $database = new \Database();
213
            $connection = $database->connect(
214
                $dbParams,
215
                $_configuration['root_sys'],
216
                $_configuration['root_sys'],
217
                true
218
            );
219
        } catch (Exception $e) {
220
            echo 'Side connection failure with '.$_configuration['db_host'].', '.$_configuration['db_user'].', ******** ';
221
            exit();
222
        }
223
224
        return $connection;
225
    }
226
227
    /**
228
     * @param string $url
229
     */
230
    public static function redirect($url)
231
    {
232
        if (preg_match('#https?://#', $url)) {
233
            header('location: '.$url);
234
        } else {
235
            header('location: '.api_get_path(WEB_PATH).$url);
236
        }
237
        exit;
238
    }
239
240
    /**
241
     * @param string $course_folder
242
     *
243
     * @return string
244
     */
245
    public static function getHtaccessFragment($course_folder)
246
    {
247
        $str = "
248
        # Change this file to fit your configuration and save it as .htaccess in the courses folder #
249
        # Chamilo mod rewrite
250
        # Comment lines start with # and are not processed
251
        
252
        <IfModule mod_rewrite.c>
253
        RewriteEngine On
254
        
255
        # Rewrite base is the dir chamilo is installed in with trailing slash
256
        RewriteBase /{$course_folder}/
257
        
258
        # Do not rewrite on the main dir
259
        # Change this path to the path of your main folder
260
        RewriteCond %{REQUEST_URI} !^/main/
261
        
262
        #replace nasty ampersands by 3 slashes, we change these back in download.php
263
        RewriteRule ([^/]+)/document/(.*)&(.*)$ $1/document/$2///$3 [N]
264
        
265
        # Rewrite everything in the scorm folder of a course to the download script
266
        RewriteRule ([^/]+)/scorm/(.*)$ /main/document/download_scorm.php?doc_url=/$2&cDir=$1 [QSA,L]
267
        
268
        # Rewrite everything in the document folder of a course to the download script
269
        RewriteRule ([^/]+)/document/(.*)$ /main/document/download.php?doc_url=/$2&cDir=$1 [QSA,L]
270
        
271
        # Rewrite everything in the work folder
272
        RewriteRule ([^/]+)/work/(.*)$ /main/work/download.php?file=work/$2&cDir=$1 [QSA,L]
273
        </IfModule>
274
        ";
275
276
        return $str;
277
    }
278
279
    /**
280
     * @return string
281
     */
282
    public static function getDefaultCourseIndexFragment()
283
    {
284
        return "<html><head></head><body></body></html>";
285
    }
286
287
    /**
288
     * @param string $template
289
     *
290
     * @return bool
291
     */
292
    public static function templateExists($template)
293
    {
294
        global $_configuration;
295
296
        // Find and checktemplate directory (files and SQL).
297
        $separator = DIRECTORY_SEPARATOR;
298
        $templatefoldername = 'plugin'.$separator.'vchamilo'.$separator.'templates';
299
        $relative_datadir = $templatefoldername.$separator.$template.'_sql';
300
        $absolute_datadir = $_configuration['root_sys'].$relative_datadir;
301
302
        return is_dir($absolute_datadir);
303
    }
304
305
    /**
306
     * drop a vchamilo instance databases using the physical connection.
307
     *
308
     * @param stdClass $params
309
     *                         return an array of errors or false if ok
310
     */
311
    public static function dropDatabase($params)
312
    {
313
        $params = clone $params;
314
315
        if (empty($params->main_database)) {
316
            Display::addFlash(Display::return_message('No database found'));
317
318
            return;
319
        }
320
321
        $databaseToDelete = $params->main_database;
322
        unset($params->main_database);
323
        $connection = self::getConnectionFromInstance($params);
324
        if ($connection) {
325
            $databases = $connection->getSchemaManager()->listDatabases();
326
327
            if (in_array($databaseToDelete, $databases)) {
328
                $connection->getSchemaManager()->dropDatabase(
329
                    $databaseToDelete
330
                );
331
                Display::addFlash(
332
                    Display::return_message(
333
                        'Database deleted: '.$databaseToDelete
334
                    )
335
                );
336
            } else {
337
                Display::addFlash(
338
                    Display::return_message(
339
                        'Database does not exist: '.$databaseToDelete
340
                    )
341
                );
342
            }
343
        } else {
344
            Display::addFlash(
345
                Display::return_message(
346
                    "Cannot connect DB: $databaseToDelete"
347
                )
348
            );
349
        }
350
351
        return false;
352
    }
353
354
    /**
355
     * @param stdClass $params
356
     *
357
     * @return bool
358
     */
359
    public static function createDatabase($params)
360
    {
361
        $params = clone $params;
362
        $databaseName = $params->main_database;
363
        unset($params->main_database);
364
365
        $connection = self::getConnectionFromInstance($params);
366
        if ($connection) {
367
            $databaseList = $connection->getSchemaManager()->listDatabases();
368
369
            if (!in_array($databaseName, $databaseList)) {
370
                $connection->getSchemaManager()->createDatabase($databaseName);
371
                Display::addFlash(
372
                    Display::return_message("Creating DB ".$databaseName)
373
                );
374
            } else {
375
                Display::addFlash(
376
                    Display::return_message("DB already exists: ".$databaseName)
377
                );
378
            }
379
380
            return true;
381
        }
382
383
        return false;
384
    }
385
386
    /**
387
     * get a proper SQLdump command.
388
     *
389
     * @param object $vchamilodata the complete new host information
390
     *
391
     * @return string the shell command
392
     */
393
    public static function getDatabaseDumpCmd($vchamilodata)
394
    {
395
        $pgm = self::getConfig('vchamilo', 'mysql_cmd');
396
397
        if (!$pgm) {
398
            $pgm = '/usr/bin/mysql';
399
        }
400
401
        $phppgm = str_replace("\\", '/', $pgm);
402
        $phppgm = str_replace("\"", '', $phppgm);
403
        $pgm = str_replace("/", DIRECTORY_SEPARATOR, $pgm);
404
405
        if (!is_executable($phppgm)) {
406
            throw new Exception('databasecommanddoesnotmatchanexecutablefile');
407
        }
408
409
        // Retrieves the host configuration (more secure).
410
        $vchamilodata = empty($vchamilodata) ? self::makeThis() : $vchamilodata;
411
        if (strstr($vchamilodata->db_host, ':') !== false) {
412
            list($vchamilodata->db_host, $vchamilodata->db_port) = explode(
413
                ':',
414
                $vchamilodata->db_host
415
            );
416
        }
417
418
        // Password.
419
        $databasePassword = '';
420
        if (!empty($vchamilodata->db_password)) {
421
            $databasePassword = '-p'.escapeshellarg($vchamilodata->db_password).' ';
422
        }
423
424
        // Making the command line (see 'vconfig.php' file for defining the right paths).
425
        $sqlcmd = $pgm.' -h'.$vchamilodata->db_host.(isset($vchamilodata->db_port) ? ' -P'.$vchamilodata->db_port.' ' : ' ');
426
        $sqlcmd .= '-u'.$vchamilodata->db_user.' '.$databasePassword;
427
        $sqlcmd .= '%DATABASE% < ';
428
429
        return $sqlcmd;
430
    }
431
432
    /**
433
     * @param stdClass $vchamilo
434
     * @param string   $template
435
     *
436
     * @return bool
437
     */
438
    public static function loadDbTemplate($vchamilo, $template)
439
    {
440
        global $_configuration;
441
442
        // Make template directory (files and SQL).
443
        $separator = DIRECTORY_SEPARATOR;
444
        $templatefoldername = 'plugin'.$separator.'vchamilo'.$separator.'templates';
445
        $absolute_datadir = $_configuration['root_sys'].$templatefoldername.$separator.$template.$separator.'dump.sql';
446
447
        if (!$sqlcmd = self::getDatabaseDumpCmd($vchamilo)) {
448
            return false;
449
        }
450
451
        $sqlcmd = str_replace('%DATABASE%', $vchamilo->main_database, $sqlcmd);
452
453
        // Make final commands to execute, depending on the database type.
454
        $import = $sqlcmd.$absolute_datadir;
455
456
        // Execute the command.
457
        Display::addFlash(Display::return_message("Load database from template dump: \n $import "));
458
459
        if (!defined('CLI_SCRIPT')) {
460
            putenv('LANG=en_US.utf-8');
461
        }
462
        // ensure utf8 is correctly handled by php exec()
463
        // @see http://stackoverflow.com/questions/10028925/call-a-program-via-shell-exec-with-utf-8-text-input
464
465
        exec($import, $output, $return);
466
        if (!empty($output)) {
467
            Display::addFlash(Display::return_message(implode("\n", $output)."\n"));
468
        }
469
470
        return true;
471
    }
472
473
    /**
474
     * Backups a database for having a snapshot.
475
     *
476
     * @param $vchamilo      object        The Vchamilo object
477
     * @param $outputfilerad string        The output SQL file radical
478
     *
479
     * @return bool if TRUE, dumping database was a success, otherwise FALSE
480
     */
481
    public static function backupDatabase($vchamilo, $outputfilerad)
482
    {
483
        // Separating host and port, if sticked.
484
        if (strstr($vchamilo->db_host, ':') !== false) {
485
            list($host, $port) = explode(':', $vchamilo->db_host);
486
        } else {
487
            $host = $vchamilo->db_host;
488
        }
489
490
        // By default, empty password.
491
        $pass = '';
492
        $pgm = null;
493
494
        if (empty($port)) {
495
            $port = 3306;
496
        }
497
498
        // Password.
499
        if (!empty($vchamilo->db_password)) {
500
            $pass = "-p".escapeshellarg($vchamilo->db_password);
501
        }
502
503
        // Making the commands for each database.
504
        $cmds = [];
505
        // Windows environments are not supported for this plugin at this time
506
        //if ($CFG->ostype == 'WINDOWS') {
507
        //    $cmd_main = "-h{$host} -P{$port} -u{$vchamilo->db_user} {$pass} {$vchamilo->main_database}";
508
        //    $cmds[] = $cmd_main . ' > ' . $outputfilerad;
509
        //} else {
510
        $cmd_main = "-h{$host} -P{$port} -u{$vchamilo->db_user} {$pass} {$vchamilo->main_database}";
511
        $cmds[] = $cmd_main.' > '.escapeshellarg($outputfilerad);
512
        //}
513
514
        $mysqldumpcmd = self::getConfig('vchamilo', 'cmd_mysqldump', true);
515
516
        $pgm = !empty($mysqldumpcmd) ? stripslashes($mysqldumpcmd) : false;
517
518
        if (!$pgm) {
519
            $message = "Database dump command not available check here: ";
520
            $url = api_get_path(WEB_CODE_PATH).'admin/configure_plugin.php?name=vchamilo';
521
            $message .= Display::url($url, $url);
522
            Display::addFlash(Display::return_message($message));
523
524
            return false;
525
        } else {
526
            $phppgm = str_replace("\\", '/', $pgm);
527
            $phppgm = str_replace("\"", '', $phppgm);
528
            $pgm = str_replace('/', DIRECTORY_SEPARATOR, $pgm);
529
530
            if (!is_executable($phppgm)) {
531
                $message = "Database dump command $phppgm does not match any executable";
532
                Display::addFlash(Display::return_message($message));
533
534
                return false;
535
            }
536
537
            // executing all commands
538
            foreach ($cmds as $cmd) {
539
                // Final command.
540
                $cmd = $pgm.' '.$cmd;
541
542
                // Executes the SQL command.
543
                exec($cmd, $execoutput, $returnvalue);
544
            }
545
        }
546
547
        // End with success.
548
        return 1;
549
    }
550
551
    /**
552
     * read manifest values in vchamilo template.
553
     */
554
    public static function getVmanifest($version)
555
    {
556
        $templatewwwroot = '';
557
        // Define the $templatewwwroot content, found in manifest.php for this template
558
        $file = api_get_path(SYS_PATH).'/plugin/vchamilo/templates/'.$version.'/manifest.php';
559
        if (file_exists($file)) {
560
            include $file;
561
562
            $manifest = new stdClass();
563
            $manifest->templatewwwroot = $templatewwwroot;
564
            //    $manifest->templatevdbprefix = $templatevdbprefix;
565
            //    $manifest->coursefolder = $coursefolder;
566
567
            return $manifest;
568
        }
569
570
        return false;
571
    }
572
573
    /**
574
     * make a fake vchamilo that represents the current host.
575
     */
576
    public static function makeThis()
577
    {
578
        global $_configuration;
579
580
        $thisPortal = new stdClass();
581
        $thisPortal->root_web = $_configuration['root_web'];
582
        $thisPortal->db_host = $_configuration['db_host'];
583
        $thisPortal->db_user = $_configuration['db_user'];
584
        $thisPortal->db_password = $_configuration['db_password'];
585
        $thisPortal->main_database = $_configuration['main_database'];
586
587
        return $thisPortal;
588
    }
589
590
    /**
591
     * Get available templates for defining a new virtual host.
592
     *
593
     * @return array the available templates, or EMPTY array
594
     */
595
    public static function getAvailableTemplates()
596
    {
597
        global $_configuration;
598
599
        $separator = DIRECTORY_SEPARATOR;
600
601
        $templatefoldername = 'plugin'.$separator.'vchamilo'.$separator.'templates';
602
        $tempDir = $_configuration['root_sys'].$templatefoldername;
603
604
        // Scans the templates.
605
        if (!is_dir($tempDir)) {
606
            $mode = api_get_permissions_for_new_directories();
607
            mkdir($tempDir, $mode, true);
608
        }
609
610
        $finder = new \Symfony\Component\Finder\Finder();
611
        $dirs = $finder->in($tempDir)->depth('== 0');
612
613
        // Retrieves template(s) name(s). Should be hostnames.
614
        $templates = [];
615
        /*if ($addEmptyTemplate) {
616
            $templates = array('' => $plugin->get_lang('emptysite'));
617
        }*/
618
619
        $template = self::getConfig('vchamilo', 'default_template');
620
621
        if ($dirs) {
622
            /** @var Symfony\Component\Finder\SplFileInfo $dir */
623
            foreach ($dirs as $dir) {
624
                if (is_dir($dir->getPathname())) {
625
                    // A template is considered when a dump.sql exists.
626
                    if (file_exists($dir->getPathname().'/dump.sql')) {
627
                        $templateName = $dir->getRelativePathname();
628
                        if ($templateName == $template) {
629
                            $templateName .= ' (default)';
630
                        }
631
                        $templates[$dir->getRelativePathname()] = $templateName;
632
                    }
633
                }
634
            }
635
        }
636
637
        return $templates;
638
    }
639
640
    /**
641
     * this function set will map standard moodle API calls to chamilo
642
     * internal primitives. This avoids too many changes to do in imported
643
     * code.
644
     */
645
    public static function getConfig($module, $key, $isplugin = true)
646
    {
647
        if ($isplugin) {
648
            $key = $module.'_'.$key;
649
        }
650
651
        $params = ['variable = ? AND subkey = ?' => [$key, $module]];
652
        $result = api_get_settings_params_simple($params);
653
        if ($result) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
654
            return $result['selected_value'];
655
        }
656
657
        return false;
658
    }
659
660
    /**
661
     * @param stdClass $vchamilo
662
     * @param string   $template
663
     */
664
    public static function loadFilesFromTemplate($vchamilo, $template)
665
    {
666
        global $_configuration;
667
668
        // Make template directory (files and SQL).
669
        $separator = DIRECTORY_SEPARATOR;
670
        $templateDir = $_configuration['root_sys'].'plugin'.$separator.'vchamilo'.$separator.'templates'.$separator.$template;
671
        $vchamilo->virtual = true;
672
        $coursePath = self::getConfig('vchamilo', 'course_real_root').$separator.$vchamilo->slug;
0 ignored issues
show
Are you sure self::getConfig('vchamilo', 'course_real_root') of type false|mixed can be used in concatenation? ( Ignorable by Annotation )

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

672
        $coursePath = /** @scrutinizer ignore-type */ self::getConfig('vchamilo', 'course_real_root').$separator.$vchamilo->slug;
Loading history...
673
        $homePath = self::getConfig('vchamilo', 'home_real_root').$separator.$vchamilo->slug;
0 ignored issues
show
Are you sure self::getConfig('vchamilo', 'home_real_root') of type false|mixed can be used in concatenation? ( Ignorable by Annotation )

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

673
        $homePath = /** @scrutinizer ignore-type */ self::getConfig('vchamilo', 'home_real_root').$separator.$vchamilo->slug;
Loading history...
674
        $archivePath = self::getConfig('vchamilo', 'archive_real_root').$separator.$vchamilo->slug;
0 ignored issues
show
Are you sure self::getConfig('vchamilo', 'archive_real_root') of type false|mixed can be used in concatenation? ( Ignorable by Annotation )

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

674
        $archivePath = /** @scrutinizer ignore-type */ self::getConfig('vchamilo', 'archive_real_root').$separator.$vchamilo->slug;
Loading history...
675
        $uploadPath = self::getConfig('vchamilo', 'upload_real_root').$separator.$vchamilo->slug;
0 ignored issues
show
Are you sure self::getConfig('vchamilo', 'upload_real_root') of type false|mixed can be used in concatenation? ( Ignorable by Annotation )

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

675
        $uploadPath = /** @scrutinizer ignore-type */ self::getConfig('vchamilo', 'upload_real_root').$separator.$vchamilo->slug;
Loading history...
676
677
        // get the protocol free hostname
678
        Display::addFlash(
679
            Display::return_message("Copying {$templateDir}/data/courses => $coursePath")
680
        );
681
682
        copyDirTo(
683
            self::chopLastSlash($templateDir.'/data/courses'),
684
            self::chopLastSlash($coursePath),
685
            false
686
        );
687
688
        Display::addFlash(
689
            Display::return_message("Copying {$templateDir}/data/archive => $archivePath")
690
        );
691
692
        copyDirTo(
693
            self::chopLastSlash($templateDir.'/data/archive'),
694
            self::chopLastSlash($archivePath),
695
            false
696
        );
697
698
        Display::addFlash(
699
            Display::return_message("Copying {$templateDir}/data/home => $homePath")
700
        );
701
702
        copyDirTo(
703
            self::chopLastSlash($templateDir.'/data/home'),
704
            self::chopLastSlash($homePath),
705
            false
706
        );
707
708
        // Upload
709
        Display::addFlash(
710
            Display::return_message("Copying {$templateDir}/data/upload => $uploadPath")
711
        );
712
713
        copyDirTo(
714
            self::chopLastSlash($templateDir.'/data/upload/'),
715
            self::chopLastSlash($uploadPath),
716
            false
717
        );
718
    }
719
720
    /**
721
     * @param string $path
722
     *
723
     * @return mixed
724
     */
725
    public static function chopLastSlash($path)
726
    {
727
        return preg_replace('/\/$/', '', $path);
728
    }
729
730
    /**
731
     * @param string $str
732
     */
733
    public static function ctrace($str)
734
    {
735
        error_log($str);
736
        Display::addFlash(Display::return_message($str, 'normal', false));
737
    }
738
739
    /**
740
     * @param $file
741
     * @param $component
742
     * @param bool $return
743
     *
744
     * @return string
745
     */
746
    public static function requireJs($file, $component, $return = false)
747
    {
748
        global $_configuration, $htmlHeadXtra;
749
750
        if (preg_match('/^local_/', $component)) {
751
            $component = str_replace('local_', '', $component);
752
            $path = 'local/';
753
        } else {
754
            $path = 'plugin/';
755
        }
756
757
        // Secure the postslashing of the roots.
758
        $root_web = $_configuration['root_web'].'/';
759
        $root_web = preg_replace('#//$#', '/', $root_web);
760
761
        $str = '<script type="text/javascript" src="'.$root_web.$path.$component.'/js/'.$file.'"></script>'."\n";
762
        if ($return === 'head') {
763
            $htmlHeadXtra[] = $str;
764
        }
765
766
        if ($return) {
767
            return $str;
768
        }
769
        echo $str;
770
    }
771
772
    /**
773
     * @param $file
774
     * @param $component
775
     * @param bool $return
776
     *
777
     * @return string
778
     */
779
    public static function requireCss($file, $component, $return = false)
780
    {
781
        global $_configuration, $htmlHeadXtra;
782
783
        if (preg_match('/^local_/', $component)) {
784
            $component = str_replace('local_', '', $component);
785
            $path = 'local/';
786
        } else {
787
            $path = 'plugin/';
788
        }
789
790
        // Secure the postslashing of the roots.
791
        $root_web = $_configuration['root_web'].'/';
792
        $root_web = preg_replace('#//$#', '/', $root_web);
793
794
        $str = '<link rel="stylesheet" type="text/css" href="'.$root_web.$path.$component.'/'.$file.'.css" />'."\n";
795
        if ($return === 'head') {
796
            $htmlHeadXtra[] = $str;
797
        }
798
        if ($return) {
799
            return $str;
800
        }
801
        echo $str;
802
    }
803
804
    /**
805
     * @param string $url
806
     *
807
     * @return string
808
     */
809
    public static function getSlugFromUrl($url)
810
    {
811
        $slugify = new Slugify();
812
        $urlInfo = parse_url($url);
813
        if (isset($urlInfo['host'])) {
814
            $path = $urlInfo['path'] != '/' ? '_'.$urlInfo['path'] : '';
815
816
            return $slugify->slugify($urlInfo['host'].$path);
817
        }
818
819
        return false;
820
    }
821
822
    /**
823
     * Check if all settings are complete.
824
     */
825
    public static function checkSettings()
826
    {
827
        $enabled = self::getConfig('vchamilo', 'enable_virtualisation');
828
829
        if (empty($enabled)) {
830
            api_not_allowed(true, 'Plugin is not enabled');
831
        }
832
833
        global $virtualChamilo;
834
        if (!isset($virtualChamilo)) {
835
            api_not_allowed(
836
                true,
837
                'You have to edit the configuration.php. Please check the readme file.'
838
            );
839
        }
840
841
        $coursePath = self::getConfig('vchamilo', 'course_real_root');
842
        $homePath = self::getConfig('vchamilo', 'home_real_root');
843
        $archivePath = self::getConfig('vchamilo', 'archive_real_root');
844
        $uploadPath = self::getConfig('vchamilo', 'upload_real_root');
845
        $cmdSql = self::getConfig('vchamilo', 'cmd_mysql');
846
        $cmdMySql = self::getConfig('vchamilo', 'cmd_mysqldump');
847
848
        if (empty($coursePath) || empty($homePath) || empty($uploadPath) || empty($archivePath) || empty($cmdSql) || empty($cmdMySql)) {
849
            api_not_allowed(true, 'You have to complete all plugin settings.');
850
        }
851
852
        $separator = DIRECTORY_SEPARATOR;
853
        $templatePath = api_get_path(SYS_PATH).'plugin'.$separator.'vchamilo'.$separator.'templates';
854
855
        $paths = [
856
            $coursePath,
857
            $homePath,
858
            $archivePath,
859
            $uploadPath,
860
            $templatePath,
861
        ];
862
863
        foreach ($paths as $path) {
864
            $path = trim($path);
865
            if (is_dir($path)) {
866
                if (!is_writable($path)) {
867
                    Display::addFlash(
868
                        Display::return_message("Directory must have writable permissions: '$path'", 'warning')
869
                    );
870
                }
871
            } else {
872
                Display::addFlash(
873
                    Display::return_message("Directory doesn't exist: '$path'", 'warning')
874
                );
875
            }
876
        }
877
    }
878
879
    /**
880
     * @param object $instance
881
     *
882
     * @return bool|\Doctrine\DBAL\Connection
883
     */
884
    public static function getConnectionFromInstance($instance, $getManager = false)
885
    {
886
        $dbParams = [
887
            'driver' => 'pdo_mysql',
888
            'host' => $instance->db_host,
889
            'user' => $instance->db_user,
890
            'password' => $instance->db_password,
891
            //'dbname' => $instance->main_database,
892
            // Only relevant for pdo_sqlite, specifies the path to the SQLite database.
893
            //'path' => isset($_configuration['db_path']) ? $_configuration['db_path'] : '',
894
            // Only relevant for pdo_mysql, pdo_pgsql, and pdo_oci/oci8,
895
            //'port' => isset($_configuration['db_port']) ? $_configuration['db_port'] : '',
896
        ];
897
898
        if (!empty($instance->main_database)) {
899
            $dbParams['dbname'] = $instance->main_database;
900
        }
901
902
        try {
903
            $database = new \Database();
904
            $manager = $database->connect(
905
                $dbParams,
906
                api_get_configuration_value('root_sys'),
907
                api_get_configuration_value('root_sys'),
908
                false,
909
                true
910
            );
911
912
            if ($getManager) {
913
                return $manager;
914
            }
915
916
            return $manager->getConnection();
917
        } catch (Exception $e) {
918
            error_log($e->getMessage());
919
        }
920
921
        return false;
922
    }
923
924
    /**
925
     * @param $data
926
     */
927
    public static function addInstance($data)
928
    {
929
        if (isset($data->what)) {
930
            unset($data->what);
931
        }
932
        if (isset($data->submitbutton)) {
933
            unset($data->submitbutton);
934
        }
935
        if (isset($data->id)) {
936
            unset($data->id);
937
        }
938
        if (isset($data->vid)) {
939
            unset($data->vid);
940
        }
941
        if (isset($data->testconnection)) {
942
            unset($data->testconnection);
943
        }
944
        if (isset($data->testdatapath)) {
945
            unset($data->testdatapath);
946
        }
947
948
        $registeronly = $data->registeronly;
949
        unset($data->registeronly);
950
        $data->lastcron = 0;
951
        $data->lastcrongap = 0;
952
        $data->croncount = 0;
953
954
        if (isset($data->template) && !empty($data->template)) {
955
            $template = $data->template;
956
        } else {
957
            $template = '';
958
        }
959
960
        $mainDatabase = api_get_configuration_value('main_database');
961
962
        if ($mainDatabase == $data->main_database) {
963
            Display::addFlash(
964
                Display::return_message('You cannot use the same database as the chamilo master', 'error')
965
            );
966
967
            return;
968
        }
969
970
        $databaseName = $data->main_database;
971
        $data->main_database = '';
972
        $connection = self::getConnectionFromInstance($data);
973
        $data->main_database = $databaseName;
974
        if (!$connection) {
975
            Display::addFlash(
976
                Display::return_message(
977
                    'Cannot connect to database with params: '.print_r($data, 1),
0 ignored issues
show
Are you sure print_r($data, 1) of type string|true can be used in concatenation? ( Ignorable by Annotation )

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

977
                    'Cannot connect to database with params: './** @scrutinizer ignore-type */ print_r($data, 1),
Loading history...
978
                    'error'
979
                )
980
            );
981
982
            return;
983
        }
984
985
        $data->root_web = api_add_trailing_slash($data->root_web);
986
987
        $data->archive_url = api_add_trailing_slash($data->archive_url);
988
        $data->home_url = api_add_trailing_slash($data->home_url);
989
        $data->upload_url = api_add_trailing_slash($data->upload_url);
990
        $data->course_url = api_add_trailing_slash($data->course_url);
991
992
        if (substr($data->root_web, 0, 4) != 'http') {
993
            $data->root_web = api_get_protocol().'://'.$data->root_web;
994
        }
995
996
        self::ctrace('Registering: '.$data->root_web);
997
        $tablename = Database::get_main_table('vchamilo');
998
        $sql = "SELECT * FROM $tablename 
999
                WHERE root_web = '".Database::escape_string($data->root_web)."'";
1000
        $result = Database::query($sql);
1001
1002
        if (Database::num_rows($result)) {
1003
            Database::update($tablename, $data, ['root_web = ?' => $data->root_web]);
1004
            $virtualInfo = Database::fetch_array($result);
1005
            $slug = $virtualInfo['slug'];
1006
        } else {
1007
            $slug = $data->slug = self::getSlugFromUrl($data->root_web);
1008
            if (empty($slug)) {
1009
                Display::addFlash(
1010
                    Display::return_message('Cannot create slug from url: '.$data->root_web, 'error')
1011
                );
1012
1013
                return;
1014
            }
1015
            Database::insert($tablename, (array) $data);
1016
        }
1017
1018
        if ($registeronly) {
1019
            // Stop it now.
1020
            self::ctrace('Registering only. out.');
1021
            self::redirect(api_get_path(WEB_PLUGIN_PATH).'vchamilo/views/manage.php');
1022
        }
1023
1024
        // or we continue with physical creation
1025
        self::createDirsFromSlug($slug);
1026
1027
        if (!$template) {
1028
            // Create empty database for install
1029
            self::ctrace("Creating database");
1030
            self::createDatabase($data);
1031
        } else {
1032
            // Deploy template database
1033
            self::ctrace("Creating databases from template '$template'");
1034
            self::createDatabase($data);
1035
            self::ctrace("Loading data template '$template'");
1036
            self::loadDbTemplate($data, $template);
1037
            self::ctrace("Coying files from template '$template'");
1038
            self::loadFilesFromTemplate($data, $template);
1039
        }
1040
1041
        // pluging in site name institution
1042
        $settingstable = $data->main_database.'.settings_current';
1043
        $accessurltable = $data->main_database.'.access_url';
1044
1045
        $sitename = Database::escape_string($data->sitename);
1046
        $institution = Database::escape_string($data->institution);
1047
1048
        $sqls[] = "UPDATE {$settingstable} SET selected_value = '{$sitename}' 
1049
                   WHERE variable = 'siteName' AND category = 'Platform' ";
1050
1051
        $sqls[] = "UPDATE {$settingstable} SET selected_value = '{$institution}' 
1052
                   WHERE variable = 'institution' AND category = 'Platform' ";
1053
1054
        $sqls[] = "UPDATE {$accessurltable} SET url = '{$data->root_web}' WHERE id = '1' ";
1055
1056
        foreach ($sqls as $sql) {
1057
            Database::query($sql);
1058
        }
1059
1060
        self::ctrace("Finished");
1061
    }
1062
1063
    /**
1064
     * @param stdClass $data
1065
     * @param string   $fromVersion
1066
     */
1067
    public static function importInstance($data, $fromVersion)
1068
    {
1069
        if (isset($data->what)) {
1070
            unset($data->what);
1071
        }
1072
        if (isset($data->submitbutton)) {
1073
            unset($data->submitbutton);
1074
        }
1075
        if (isset($data->id)) {
1076
            unset($data->id);
1077
        }
1078
        if (isset($data->vid)) {
1079
            unset($data->vid);
1080
        }
1081
        if (isset($data->testconnection)) {
1082
            unset($data->testconnection);
1083
        }
1084
        if (isset($data->testdatapath)) {
1085
            unset($data->testdatapath);
1086
        }
1087
1088
        $fromCoursePath = $data->course_path;
1089
        $fromHomePath = $data->home_path;
1090
        $fromUploadPath = $data->upload_path;
1091
1092
        unset($data->course_path);
1093
        unset($data->home_path);
1094
        unset($data->upload_path);
1095
1096
        $newDatabase = clone $data;
1097
        $newDatabase->main_database = $newDatabase->import_to_main_database;
1098
        $newDatabase->db_user = $newDatabase->import_to_db_user;
1099
        $newDatabase->db_password = $newDatabase->import_to_db_password;
1100
        $newDatabase->db_host = $newDatabase->import_to_db_host;
1101
1102
        unset($newDatabase->import_to_main_database);
1103
        unset($newDatabase->import_to_db_user);
1104
        unset($newDatabase->import_to_db_password);
1105
        unset($newDatabase->import_to_db_host);
1106
1107
        unset($data->import_to_main_database);
1108
        unset($data->import_to_db_user);
1109
        unset($data->import_to_db_password);
1110
        unset($data->import_to_db_host);
1111
1112
        $data->lastcron = 0;
1113
        $data->lastcrongap = 0;
1114
        $data->croncount = 0;
1115
1116
        $mainDatabase = api_get_configuration_value('main_database');
1117
1118
        if ($mainDatabase == $data->main_database) {
1119
            Display::addFlash(
1120
                Display::return_message('You cannot use the same database as the chamilo master', 'error')
1121
            );
1122
1123
            return false;
1124
        }
1125
1126
        self::ctrace('Registering: '.$data->root_web);
1127
1128
        $table = Database::get_main_table('vchamilo');
1129
        $sql = "SELECT * FROM $table 
1130
                WHERE root_web = '".Database::escape_string($data->root_web)."'";
1131
        $result = Database::query($sql);
1132
        $id = null;
1133
        if (Database::num_rows($result)) {
1134
            Display::addFlash(
1135
                Display::return_message('Instance was already added: '.$data->root_web, 'error')
1136
            );
1137
1138
            return false;
1139
        } else {
1140
            /** @var EntityManager $em */
1141
            $em = self::getConnectionFromInstance($data, true);
1142
            if ($em) {
1143
                $connection = $em->getConnection();
1144
                $statement = $connection->query('SELECT * FROM settings_current');
1145
                $settings = $statement->fetchAll();
1146
                $settings = array_column(
1147
                    $settings,
1148
                    'selected_value',
1149
                    'variable'
1150
                );
1151
                $institution = $settings['Institution'];
1152
                $siteName = $settings['siteName'];
1153
                $newDatabase->sitename = $siteName;
1154
                $newDatabase->institution = $institution;
1155
                $slug = $newDatabase->slug = $data->slug = self::getSlugFromUrl($data->root_web);
1156
                $id = Database::insert($table, (array) $newDatabase);
1157
            }
1158
        }
1159
1160
        if (!$id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $id of type false|integer is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1161
            // Show data detail to help debug
1162
            //var_dump($data);
1163
            throw new Exception('New/Imported instance was not registered - edit '.__FILE__.' on line '.__LINE__.'to var_dump');
1164
        }
1165
1166
        if (empty($slug)) {
1167
            throw new Exception('Slug is empty');
1168
        }
1169
1170
        self::createDirsFromSlug($slug);
1171
        $databaseCreated = self::createDatabase($newDatabase);
1172
        if (!$databaseCreated) {
1173
            Display::addFlash(
1174
                Display::return_message('Error while creating a DB', 'error')
1175
            );
1176
1177
            return false;
1178
        }
1179
1180
        $coursePath = self::getConfig('vchamilo', 'course_real_root').'/'.$slug;
0 ignored issues
show
Are you sure self::getConfig('vchamilo', 'course_real_root') of type false|mixed can be used in concatenation? ( Ignorable by Annotation )

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

1180
        $coursePath = /** @scrutinizer ignore-type */ self::getConfig('vchamilo', 'course_real_root').'/'.$slug;
Loading history...
1181
        $homePath = self::getConfig('vchamilo', 'home_real_root').'/'.$slug;
0 ignored issues
show
Are you sure self::getConfig('vchamilo', 'home_real_root') of type false|mixed can be used in concatenation? ( Ignorable by Annotation )

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

1181
        $homePath = /** @scrutinizer ignore-type */ self::getConfig('vchamilo', 'home_real_root').'/'.$slug;
Loading history...
1182
        $uploadPath = self::getConfig('vchamilo', 'upload_real_root').'/'.$slug;
0 ignored issues
show
Are you sure self::getConfig('vchamilo', 'upload_real_root') of type false|mixed can be used in concatenation? ( Ignorable by Annotation )

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

1182
        $uploadPath = /** @scrutinizer ignore-type */ self::getConfig('vchamilo', 'upload_real_root').'/'.$slug;
Loading history...
1183
1184
        $dumpFile = api_get_path(SYS_ARCHIVE_PATH).uniqid($data->main_database.'_dump_', true).'.sql';
1185
        self::ctrace('Create backup from "'.$data->main_database.'" here: '.$dumpFile.' ');
1186
        self::backupDatabase($data, $dumpFile);
1187
1188
        $sqlcmd = self::getDatabaseDumpCmd($newDatabase);
1189
        $sqlcmd = str_replace('%DATABASE%', $newDatabase->main_database, $sqlcmd);
1190
        $import = $sqlcmd.$dumpFile;
1191
1192
        // Execute the command.
1193
        if (!defined('CLI_SCRIPT')) {
1194
            putenv('LANG=en_US.utf-8');
1195
        }
1196
1197
        // ensure utf8 is correctly handled by php exec()
1198
        // @see http://stackoverflow.com/questions/10028925/call-a-program-via-shell-exec-with-utf-8-text-input
1199
        $result = exec($import, $output, $return);
1200
1201
        self::ctrace('Restore backup here "'.$newDatabase->main_database.'" : <br />'.$import.' ');
1202
        self::ctrace($result);
1203
1204
        $command = new \Chash\Command\Installation\UpgradeDatabaseCommand();
1205
        // Creates the helper set
1206
        $helperSet = \Doctrine\ORM\Tools\Console\ConsoleRunner::createHelperSet($em);
1207
1208
        $helpers = [
1209
            'configuration' => new Chash\Helpers\ConfigurationHelper(),
1210
            'dialog' => new \Symfony\Component\Console\Helper\QuestionHelper(),
1211
        ];
1212
1213
        foreach ($helpers as $name => $helper) {
1214
            $helperSet->set($helper, $name);
1215
        }
1216
1217
        $command->setHelperSet($helperSet);
1218
1219
        $tmpFile = tmpfile();
1220
        $outputStream = new \Symfony\Component\Console\Output\BufferedOutput($tmpFile);
1221
1222
        $arguments = [
1223
            'from-version' => $fromVersion, // @todo change value
1224
            'to-version' => '1.11.x',
1225
            'host' => $newDatabase->db_host,
1226
            'username' => $newDatabase->db_user,
1227
            'password' => $newDatabase->db_password,
1228
            'db_name' => $newDatabase->main_database,
1229
            'root_sys' => api_get_configuration_value('root_sys'),
1230
        ];
1231
1232
        $input = new ArrayInput($arguments);
1233
        $command->run($input, $outputStream);
1234
1235
        error_log($outputStream->fetch());
1236
1237
        if (file_exists($dumpFile)) {
1238
            unlink($dumpFile);
1239
        }
1240
1241
        // Course
1242
        self::ctrace("Copy from '$fromCoursePath' to backup '$coursePath' ");
1243
        copyDirTo(
1244
            self::chopLastSlash($fromCoursePath),
1245
            self::chopLastSlash($coursePath),
1246
            false
1247
        );
1248
1249
        // Home
1250
        self::ctrace("Copy from '$fromHomePath' to backup '$homePath' ");
1251
        copyDirTo(
1252
            self::chopLastSlash($fromHomePath),
1253
            self::chopLastSlash($homePath),
1254
            false
1255
        );
1256
1257
        // Upload
1258
        self::ctrace("Copy from '$fromUploadPath' to backup '$uploadPath' ");
1259
        copyDirTo(
1260
            self::chopLastSlash($fromUploadPath),
1261
            self::chopLastSlash($uploadPath),
1262
            false
1263
        );
1264
1265
        self::ctrace("Finished");
1266
    }
1267
1268
    /**
1269
     * @param string $slug
1270
     *
1271
     * @return string
1272
     */
1273
    public static function createDirsFromSlug($slug)
1274
    {
1275
        // We continue with physical creation
1276
1277
        // Create course directory for operations.
1278
        // this is very important here (DO NOT USE api_get_path() !!) because storage may be remotely located
1279
        $absAlternateCourse = self::getConfig('vchamilo', 'course_real_root');
1280
        $courseDir = $absAlternateCourse.'/'.$slug;
0 ignored issues
show
Are you sure $absAlternateCourse of type false|mixed can be used in concatenation? ( Ignorable by Annotation )

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

1280
        $courseDir = /** @scrutinizer ignore-type */ $absAlternateCourse.'/'.$slug;
Loading history...
1281
        $mode = api_get_permissions_for_new_directories();
1282
        if (!is_dir($courseDir)) {
1283
            self::ctrace("Creating physical course dir in $courseDir");
1284
            mkdir($courseDir, $mode, true);
1285
            // initiate default index
1286
            $indexFile = $courseDir.'/index.html';
1287
            if ($indexFile) {
1288
                file_put_contents($indexFile, self::getDefaultCourseIndexFragment());
1289
            }
1290
1291
            $htaccessFile = $courseDir.'/.htaccess';
1292
            if ($htaccessFile) {
1293
                file_put_contents($htaccessFile, self::getHtaccessFragment($slug));
1294
            }
1295
        }
1296
1297
        $absAlternateHome = self::getConfig('vchamilo', 'home_real_root');
1298
        $absAlternateArchive = self::getConfig('vchamilo', 'archive_real_root');
1299
        $absAlternateUpload = self::getConfig('vchamilo', 'upload_real_root');
1300
1301
        // absalternatehome is a vchamilo config setting that tells where the
1302
        // real physical storage for home pages are.
1303
        $homeDir = $absAlternateHome.'/'.$slug;
0 ignored issues
show
Are you sure $absAlternateHome of type false|mixed can be used in concatenation? ( Ignorable by Annotation )

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

1303
        $homeDir = /** @scrutinizer ignore-type */ $absAlternateHome.'/'.$slug;
Loading history...
1304
        $archiveDir = $absAlternateArchive.'/'.$slug;
0 ignored issues
show
Are you sure $absAlternateArchive of type false|mixed can be used in concatenation? ( Ignorable by Annotation )

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

1304
        $archiveDir = /** @scrutinizer ignore-type */ $absAlternateArchive.'/'.$slug;
Loading history...
1305
        $uploadDir = $absAlternateUpload.'/'.$slug;
0 ignored issues
show
Are you sure $absAlternateUpload of type false|mixed can be used in concatenation? ( Ignorable by Annotation )

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

1305
        $uploadDir = /** @scrutinizer ignore-type */ $absAlternateUpload.'/'.$slug;
Loading history...
1306
1307
        $dirs = [
1308
            $homeDir,
1309
            $archiveDir,
1310
            $uploadDir,
1311
        ];
1312
1313
        foreach ($dirs as $dir) {
1314
            self::ctrace("Making dir as $dir");
1315
1316
            if (!is_dir($dir)) {
1317
                if (!mkdir($dir, $mode, true)) {
1318
                    self::ctrace("Error creating dir $dir \n");
1319
                }
1320
            }
1321
        }
1322
    }
1323
1324
    /**
1325
     * @param $id
1326
     *
1327
     * @return array|mixed
1328
     */
1329
    public static function getInstance($id)
1330
    {
1331
        $vhost = new stdClass();
1332
        if ($id) {
1333
            $id = (int) $id;
1334
            $sql = "SELECT * FROM vchamilo WHERE id = $id";
1335
            $result = Database::query($sql);
1336
            $vhost = (object) Database::fetch_array($result, 'ASSOC');
1337
        }
1338
1339
        return $vhost;
1340
    }
1341
1342
    /**
1343
     * @param stdClass $instance
1344
     *
1345
     * @return bool|string returns the original version of the app
1346
     */
1347
    public static function canBeUpgraded($instance)
1348
    {
1349
        $connection = self::getConnectionFromInstance($instance);
1350
        if ($connection) {
1351
            $sql = 'SELECT * FROM settings_current WHERE variable = "chamilo_database_version"';
1352
            $statement = $connection->query($sql);
1353
            $settings = $statement->fetchAll();
1354
            $settings = array_column($settings, 'selected_value', 'variable');
1355
            $version = $settings['chamilo_database_version'];
1356
            $versionParts = explode('.', $version);
1357
            $version = implode('.', [$versionParts[0], $versionParts[1], '0']);
1358
1359
            $currentVersion = api_get_setting('chamilo_database_version');
1360
            $versionParts = explode('.', $currentVersion);
1361
            $currentVersion = implode(
1362
                '.',
1363
                [$versionParts[0], $versionParts[1], '0']
1364
            );
1365
1366
            if (version_compare($version, $currentVersion, '<')) {
1367
                return $version;
1368
            }
1369
        }
1370
1371
        return false;
1372
    }
1373
1374
    /**
1375
     * @return array
1376
     */
1377
    public static function getEncryptList()
1378
    {
1379
        $encryptList = [
1380
            'bcrypt',
1381
            'sha1',
1382
            'md5',
1383
            'none',
1384
        ];
1385
1386
        return array_combine($encryptList, $encryptList);
1387
    }
1388
}
1389