Completed
Push — master ( 27e209...a08afa )
by Julito
186:04 queued 150:53
created

Virtual::bootConnection()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 28
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 19
nc 8
nop 1
dl 0
loc 28
rs 8.439
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Cocur\Slugify\Slugify;
5
use Symfony\Component\Finder\Finder;
6
use Symfony\Component\Console\Input\ArrayInput;
7
use Symfony\Component\Console\Output\StreamOutput;
8
use Symfony\Component\Console\Application;
9
use Doctrine\ORM\EntityManager;
10
11
/**
12
 * Class Virtual
13
 */
14
class Virtual
15
{
16
    /**
17
     * @param array $_configuration
18
     */
19
    public static function hookConfiguration(& $_configuration)
20
    {
21
        global $virtualChamilo;
22
23
        if (defined('CLI_SCRIPT') && !defined('CLI_VCHAMILO_OVERRIDE')) {
24
            return;
25
        }
26
27
        // provides an effective value for the virtual root_web based on domain analysis
28
        self::getHostName($_configuration);
29
30
        // We are on physical chamilo. Let original config play
31
        $virtualChamiloWebRoot = rtrim($_configuration['vchamilo_web_root'], '/').'/';
32
33
        $virtualChamilo = [];
34
        if ($_configuration['root_web'] == $virtualChamiloWebRoot) {
35
            return;
36
        }
37
38
        // pre hook to chamilo main table and get alternate configuration.
39
        // sure Database object is not set up. Soo use bootstrap connection
40
        /** @var \Doctrine\DBAL\Connection $connection */
41
        $connection = self::bootConnection($_configuration);
42
43
        $query = "SELECT * FROM vchamilo WHERE root_web = '$virtualChamiloWebRoot'";
44
        $result = $connection->executeQuery($query);
45
46
        if ($result->rowCount()) {
47
            $data = $result->fetch();
48
            $excludes = ['id', 'name'];
49
            $query = "SELECT * FROM settings_current WHERE subkey = 'vchamilo'";
50
            $virtualSettings = $connection->executeQuery($query);
51
            $virtualSettings = $virtualSettings->fetchAll();
52
53
            $homePath = '';
54
            $coursePath = '';
55
            $archivePath = '';
56
            $uploadPath = '';
57
            $passwordEncryption = '';
58
            foreach ($virtualSettings as $setting) {
59
                switch ($setting['variable']) {
60
                    case 'vchamilo_upload_real_root':
61
                        $uploadPath = $setting['selected_value'];
62
                        break;
63
                    case 'vchamilo_home_real_root':
64
                        $homePath = $setting['selected_value'];
65
                        break;
66
                    case 'vchamilo_course_real_root':
67
                        $coursePath = $setting['selected_value'];
68
                        break;
69
                    case 'vchamilo_archive_real_root':
70
                        $archivePath = $setting['selected_value'];
71
                        break;
72
                    case 'vchamilo_password_encryption':
73
                        $passwordEncryption = $setting['selected_value'];
74
                        break;
75
                }
76
            }
77
78
            if (empty($homePath) || empty($coursePath) || empty($archivePath) || empty($uploadPath)) {
79
                echo 'Configure correctly the vchamilo plugin';
80
                exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
81
            }
82
83
            // Only load if is visible
84
            if ($data && $data['visible'] === '1') {
85
                foreach ($data as $key => $value) {
86
                    if (!in_array($key, $excludes)) {
87
                        // Avoid empty password_encryption
88
                        if ($key == 'password_encryption' && empty($value)) {
89
                            continue;
90
                        }
91
                        $_configuration[$key] = $value;
92
                    }
93
                    $_configuration['virtual'] = $data['root_web'].'/';
94
                }
95
96
                $data['SYS_ARCHIVE_PATH'] = self::addTrailingSlash($archivePath).$data['slug'];
97
                $data['SYS_HOME_PATH'] = self::addTrailingSlash($homePath).$data['slug'];
98
                $data['SYS_COURSE_PATH'] = self::addTrailingSlash($coursePath).$data['slug'];
99
                $data['SYS_UPLOAD_PATH'] = self::addTrailingSlash($uploadPath).$data['slug'];
100
101
                $data['WEB_HOME_PATH'] = self::addTrailingSlash($data['home_url']);
102
                $data['WEB_UPLOAD_PATH'] = self::addTrailingSlash($data['upload_url']);
103
                $data['WEB_ARCHIVE_PATH'] = self::addTrailingSlash($data['archive_url']);
104
                //$data['WEB_COURSE_PATH'] = self::addTrailingSlash($data['course_url']);
105
106
                if (!empty($passwordEncryption)) {
107
                    $_configuration['password_encryption'] = $passwordEncryption;
108
                }
109
110
                // Instance cannot have multiple urls
111
                $_configuration['multiple_access_urls'] = false;
112
                $_configuration['virtual_css_theme_folder'] = '';
113
                if (isset($data['css_theme_folder']) && !empty($data['css_theme_folder'])) {
114
                    $_configuration['virtual_css_theme_folder'] = $data['css_theme_folder'];
115
                }
116
                $virtualChamilo = $data;
117
            } else {
118
                exit("This portal is disabled. Please contact your administrator");
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
119
            }
120
        } // otherwise it means the plugin was not configured yet
121
    }
122
123
    /**
124
     * @param array $_configuration
125
     */
126
    public static function getHostName(&$_configuration)
127
    {
128
        if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
129
            $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' || !empty($_configuration['force_https_forwarded_proto'])
130
        ) {
131
            $protocol = 'https';
132
        } else {
133
            if (!empty($_SERVER['HTTPS'])) {
134
                $protocol = 'https';
135
            } else {
136
                $protocol = 'http';
137
            }
138
        }
139
140
        if (defined('CLI_VCHAMILO_OVERRIDE')) {
141
            $_configuration['vchamilo_web_root'] = CLI_VCHAMILO_OVERRIDE;
0 ignored issues
show
Bug introduced by
The constant CLI_VCHAMILO_OVERRIDE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
142
            $_configuration['vchamilo_name'] = preg_replace('#https?://#', '', CLI_VCHAMILO_OVERRIDE);
143
            // remove radical from override for name
144
145
            // fake the server signature
146
            global $_SERVER;
147
148
            $_SERVER['SERVER_NAME'] = $_configuration['vchamilo_name'];
149
            $_SERVER['HTTP_HOST'] = $_configuration['vchamilo_name'];
150
            $_SERVER['QUERY_STRING'] = '';
151
            $_SERVER['REQUEST_URI'] = CLI_VCHAMILO_OVERRIDE;
152
153
            return;
154
        }
155
156
        $contentPrefix = '/';
157
        if (isset($_SERVER['CONTEXT_PREFIX']) && !empty($_SERVER['CONTEXT_PREFIX'])) {
158
            $contentPrefix = $_SERVER['CONTEXT_PREFIX'];
159
        } else {
160
            // Getting url_append from URL
161
            if (isset($_SERVER['REQUEST_URI'])) {
162
                $requestUri = $_SERVER['REQUEST_URI'];
163
                if (strpos($requestUri, '/courses/') !== false) {
164
                    $result = substr($requestUri, 0, strpos($requestUri, '/courses/'));
165
                    if (!empty($result) && $result != '/') {
166
                        $contentPrefix = $result;
167
                    }
168
                }
169
            }
170
        }
171
172
        $_configuration['vchamilo_web_root'] = "{$protocol}://".@$_SERVER['HTTP_HOST'].$contentPrefix;
173
174
        $_configuration['vchamilo_name'] = @$_SERVER['HTTP_HOST'];
175
        if (empty($_configuration['vchamilo_name'])) { // try again with another source if has failed
176
            $_configuration['vchamilo_name'] = "{$protocol}://".$_SERVER['SERVER_NAME'];
177
            if ($_SERVER['SERVER_PORT'] != 80) {
178
                $_configuration['vchamilo_name'] .= ':'.$_SERVER['SERVER_PORT'];
179
            }
180
            $_configuration['vchamilo_name'] = $_SERVER['SERVER_NAME'];
181
        }
182
    }
183
184
    /**
185
     * @param string $path
186
     * @return string
187
     */
188
    public static function addTrailingSlash($path)
189
    {
190
        return substr($path, -1) == '/' ? $path : $path.'/';
191
    }
192
193
    /**
194
    * provides a side connection to a vchamilo database
195
    * @param array $_configuration
196
     *
197
    * @return \Doctrine\DBAL\Driver\Connection
198
    */
199
    public static function bootConnection(&$_configuration)
200
    {
201
        $dbParams = [
202
            'driver' => 'pdo_mysql',
203
            'host' => $_configuration['db_host'],
204
            'user' => $_configuration['db_user'],
205
            'password' => $_configuration['db_password'],
206
            'dbname' => isset($_configuration['main_database']) ? $_configuration['main_database'] : '',
207
            // Only relevant for pdo_sqlite, specifies the path to the SQLite database.
208
            'path' => isset($_configuration['db_path']) ? $_configuration['db_path'] : '',
209
            // Only relevant for pdo_mysql, pdo_pgsql, and pdo_oci/oci8,
210
            'port' => isset($_configuration['db_port']) ? $_configuration['db_port'] : '',
211
        ];
212
213
        try {
214
            $database = new \Database();
215
            $connection = $database->connect(
216
                $dbParams,
217
                $_configuration['root_sys'],
218
                $_configuration['root_sys'],
219
                true
220
            );
221
        } catch (Exception $e) {
222
            echo('Side connection failure with '.$_configuration['db_host'].', '.$_configuration['db_user'].', ******** ');
223
            die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
224
        }
225
226
        return $connection;
227
    }
228
229
    /**
230
     * @param string $url
231
     */
232
    public static function redirect($url)
233
    {
234
        if (preg_match('#https?://#', $url)) {
235
            header('location: '.$url);
236
        } else {
237
            header('location: '.api_get_path(WEB_PATH).$url);
238
        }
239
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
240
    }
241
242
    /**
243
     * @param string $course_folder
244
     * @return string
245
     */
246
    public static function getHtaccessFragment($course_folder)
247
    {
248
        $str = "
249
        # Change this file to fit your configuration and save it as .htaccess in the courses folder #
250
        # Chamilo mod rewrite
251
        # Comment lines start with # and are not processed
252
        
253
        <IfModule mod_rewrite.c>
254
        RewriteEngine On
255
        
256
        # Rewrite base is the dir chamilo is installed in with trailing slash
257
        RewriteBase /{$course_folder}/
258
        
259
        # Do not rewrite on the main dir
260
        # Change this path to the path of your main folder
261
        RewriteCond %{REQUEST_URI} !^/main/
262
        
263
        #replace nasty ampersands by 3 slashes, we change these back in download.php
264
        RewriteRule ([^/]+)/document/(.*)&(.*)$ $1/document/$2///$3 [N]
265
        
266
        # Rewrite everything in the scorm folder of a course to the download script
267
        RewriteRule ([^/]+)/scorm/(.*)$ /main/document/download_scorm.php?doc_url=/$2&cDir=$1 [QSA,L]
268
        
269
        # Rewrite everything in the document folder of a course to the download script
270
        RewriteRule ([^/]+)/document/(.*)$ /main/document/download.php?doc_url=/$2&cDir=$1 [QSA,L]
271
        
272
        # Rewrite everything in the work folder
273
        RewriteRule ([^/]+)/work/(.*)$ /main/work/download.php?file=work/$2&cDir=$1 [QSA,L]
274
        </IfModule>
275
        ";
276
277
        return $str;
278
    }
279
280
    /**
281
     * @return string
282
     */
283
    public static function getDefaultCourseIndexFragment()
284
    {
285
        return "<html><head></head><body></body></html>";
286
    }
287
288
    /**
289
     * @param string $template
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
    * @param stdClass $params
308
    * return an array of errors or false if ok
309
    */
310
    public static function dropDatabase($params)
311
    {
312
        $params = clone $params;
313
314
        if (empty($params->main_database)) {
315
            Display::addFlash(Display::return_message('No database found'));
316
317
            return;
318
        }
319
320
        $databaseToDelete = $params->main_database;
321
        unset($params->main_database);
322
        $connection = self::getConnectionFromInstance($params);
323
        if ($connection) {
324
            $databases = $connection->getSchemaManager()->listDatabases();
325
326
            if (in_array($databaseToDelete, $databases)) {
327
                $connection->getSchemaManager()->dropDatabase(
328
                    $databaseToDelete
329
                );
330
                Display::addFlash(
331
                    Display::return_message(
332
                        'Database deleted: '.$databaseToDelete
333
                    )
334
                );
335
            } else {
336
                Display::addFlash(
337
                    Display::return_message(
338
                        'Database does not exist: '.$databaseToDelete
339
                    )
340
                );
341
            }
342
        } else {
343
            Display::addFlash(
344
                Display::return_message(
345
                    "Cannot connect DB: $databaseToDelete"
346
                )
347
            );
348
        }
349
350
        return false;
351
    }
352
353
    /**
354
     * @param stdClass $params
355
     * @return bool
356
     */
357
    public static function createDatabase($params)
358
    {
359
        $params = clone $params;
360
        $databaseName = $params->main_database;
361
        unset($params->main_database);
362
363
        $connection = self::getConnectionFromInstance($params);
364
        if ($connection) {
365
            $databaseList = $connection->getSchemaManager()->listDatabases();
366
367
            if (!in_array($databaseName, $databaseList)) {
368
                $connection->getSchemaManager()->createDatabase($databaseName);
369
                Display::addFlash(
370
                    Display::return_message("Creating DB ".$databaseName)
371
                );
372
            } else {
373
                Display::addFlash(
374
                    Display::return_message("DB already exists: ".$databaseName)
375
                );
376
            }
377
378
            return true;
379
        }
380
        return false;
381
    }
382
383
    /**
384
    * get a proper SQLdump command
385
    * @param object $vchamilodata the complete new host information
386
    * @return string the shell command
387
    */
388
    public static function getDatabaseDumpCmd($vchamilodata)
389
    {
390
        $pgm = self::getConfig('vchamilo', 'mysql_cmd');
391
392
        if (!$pgm) {
393
            $pgm = '/usr/bin/mysql';
394
        }
395
396
        $phppgm = str_replace("\\", '/', $pgm);
397
        $phppgm = str_replace("\"", '', $phppgm);
398
        $pgm = str_replace("/", DIRECTORY_SEPARATOR, $pgm);
399
400
        if (!is_executable($phppgm)) {
401
            throw new Exception('databasecommanddoesnotmatchanexecutablefile');
402
        }
403
404
        // Retrieves the host configuration (more secure).
405
        $vchamilodata = empty($vchamilodata) ? self::makeThis() : $vchamilodata;
406
        if (strstr($vchamilodata->db_host, ':') !== false) {
407
            list($vchamilodata->db_host, $vchamilodata->db_port) = explode(
408
                ':',
409
                $vchamilodata->db_host
410
            );
411
        }
412
413
        // Password.
414
        $databasePassword = '';
415
        if (!empty($vchamilodata->db_password)) {
416
            $databasePassword = '-p'.escapeshellarg($vchamilodata->db_password).' ';
417
        }
418
419
        // Making the command line (see 'vconfig.php' file for defining the right paths).
420
        $sqlcmd = $pgm.' -h'.$vchamilodata->db_host.(isset($vchamilodata->db_port) ? ' -P'.$vchamilodata->db_port.' ' : ' ');
421
        $sqlcmd .= '-u'.$vchamilodata->db_user.' '.$databasePassword;
422
        $sqlcmd .= '%DATABASE% < ';
423
424
        return $sqlcmd;
425
    }
426
427
    /**
428
     * @param stdClass $vchamilo
429
     * @param string $template
430
     * @return bool
431
     */
432
    public static function loadDbTemplate($vchamilo, $template)
433
    {
434
        global $_configuration;
435
436
        // Make template directory (files and SQL).
437
        $separator = DIRECTORY_SEPARATOR;
438
        $templatefoldername = 'plugin'.$separator.'vchamilo'.$separator.'templates';
439
        $absolute_datadir = $_configuration['root_sys'].$templatefoldername.$separator.$template.$separator.'dump.sql';
440
441
        if (!$sqlcmd = self::getDatabaseDumpCmd($vchamilo)) {
442
            return false;
443
        }
444
445
        $sqlcmd = str_replace('%DATABASE%', $vchamilo->main_database, $sqlcmd);
446
447
        // Make final commands to execute, depending on the database type.
448
        $import = $sqlcmd.$absolute_datadir;
449
450
        // Execute the command.
451
        Display::addFlash(Display::return_message("Load database from template dump: \n $import "));
452
453
        if (!defined('CLI_SCRIPT')) {
454
            putenv('LANG=en_US.utf-8');
455
        }
456
        // ensure utf8 is correctly handled by php exec()
457
        // @see http://stackoverflow.com/questions/10028925/call-a-program-via-shell-exec-with-utf-8-text-input
458
459
        exec($import, $output, $return);
460
        if (!empty($output)) {
461
            Display::addFlash(Display::return_message(implode("\n", $output)."\n"));
462
        }
463
464
        return true;
465
    }
466
467
    /**
468
     * Backups a database for having a snapshot.
469
     * @param        $vchamilo    object        The Vchamilo object.
470
     * @param        $outputfilerad    string        The output SQL file radical.
471
     * @return        bool    If TRUE, dumping database was a success, otherwise FALSE.
472
     */
473
    public static function backupDatabase($vchamilo, $outputfilerad)
474
    {
475
        // Separating host and port, if sticked.
476
        if (strstr($vchamilo->db_host, ':') !== false) {
477
            list($host, $port) = explode(':', $vchamilo->db_host);
478
        } else {
479
            $host = $vchamilo->db_host;
480
        }
481
482
        // By default, empty password.
483
        $pass = '';
484
        $pgm = null;
485
486
        if (empty($port)) {
487
            $port = 3306;
488
        }
489
490
        // Password.
491
        if (!empty($vchamilo->db_password)) {
492
            $pass = "-p".escapeshellarg($vchamilo->db_password);
493
        }
494
495
        // Making the commands for each database.
496
        $cmds = [];
497
        // Windows environments are not supported for this plugin at this time
498
        //if ($CFG->ostype == 'WINDOWS') {
499
        //    $cmd_main = "-h{$host} -P{$port} -u{$vchamilo->db_user} {$pass} {$vchamilo->main_database}";
500
        //    $cmds[] = $cmd_main . ' > ' . $outputfilerad;
501
        //} else {
502
        $cmd_main = "-h{$host} -P{$port} -u{$vchamilo->db_user} {$pass} {$vchamilo->main_database}";
503
        $cmds[] = $cmd_main.' > '.escapeshellarg($outputfilerad);
504
        //}
505
506
        $mysqldumpcmd = self::getConfig('vchamilo', 'cmd_mysqldump', true);
507
508
        $pgm = !empty($mysqldumpcmd) ? stripslashes($mysqldumpcmd) : false;
509
510
        if (!$pgm) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $pgm of type false|string is loosely compared to false; this is ambiguous if the string can be empty. 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 string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
511
            $message = "Database dump command not available check here: ";
512
            $url = api_get_path(WEB_CODE_PATH).'admin/configure_plugin.php?name=vchamilo';
513
            $message .= Display::url($url, $url);
514
            Display::addFlash(Display::return_message($message));
515
516
            return false;
517
        } else {
518
            $phppgm = str_replace("\\", '/', $pgm);
519
            $phppgm = str_replace("\"", '', $phppgm);
520
            $pgm = str_replace('/', DIRECTORY_SEPARATOR, $pgm);
521
522
            if (!is_executable($phppgm)) {
523
                $message = "Database dump command $phppgm does not match any executable";
524
                Display::addFlash(Display::return_message($message));
525
526
                return false;
527
            }
528
529
            // executing all commands
530
            foreach ($cmds as $cmd) {
531
                // Final command.
532
                $cmd = $pgm.' '.$cmd;
533
534
                // Executes the SQL command.
535
                exec($cmd, $execoutput, $returnvalue);
536
            }
537
        }
538
539
        // End with success.
540
        return 1;
0 ignored issues
show
Bug Best Practice introduced by
The expression return 1 returns the type integer which is incompatible with the documented return type boolean.
Loading history...
541
    }
542
543
    /**
544
    * read manifest values in vchamilo template.
545
    */
546
    public static function getVmanifest($version)
547
    {
548
        $templatewwwroot = '';
549
        // Define the $templatewwwroot content, found in manifest.php for this template
550
        $file = api_get_path(SYS_PATH).'/plugin/vchamilo/templates/'.$version.'/manifest.php';
551
        if (file_exists($file)) {
552
            include $file;
553
554
            $manifest = new stdClass();
555
            $manifest->templatewwwroot = $templatewwwroot;
556
            //    $manifest->templatevdbprefix = $templatevdbprefix;
557
            //    $manifest->coursefolder = $coursefolder;
558
559
            return $manifest;
560
        }
561
562
        return false;
563
    }
564
565
    /**
566
    * make a fake vchamilo that represents the current host
567
    */
568
    public static function makeThis()
569
    {
570
        global $_configuration;
571
572
        $thisPortal = new stdClass();
573
        $thisPortal->root_web = $_configuration['root_web'];
574
        $thisPortal->db_host = $_configuration['db_host'];
575
        $thisPortal->db_user = $_configuration['db_user'];
576
        $thisPortal->db_password = $_configuration['db_password'];
577
        $thisPortal->main_database = $_configuration['main_database'];
578
579
        return $thisPortal;
580
    }
581
582
    /**
583
     * Get available templates for defining a new virtual host.
584
     * @return        array        The available templates, or EMPTY array.
585
     */
586
    public static function getAvailableTemplates()
587
    {
588
        global $_configuration;
589
590
        $separator = DIRECTORY_SEPARATOR;
591
592
        $templatefoldername = 'plugin'.$separator.'vchamilo'.$separator.'templates';
593
        $tempDir = $_configuration['root_sys'].$templatefoldername;
594
595
        // Scans the templates.
596
        if (!is_dir($tempDir)) {
597
            mkdir($tempDir, 0777, true);
598
        }
599
600
        $finder = new \Symfony\Component\Finder\Finder();
601
        $dirs = $finder->in($tempDir)->depth('== 0');
602
603
        // Retrieves template(s) name(s). Should be hostnames.
604
        $templates = [];
605
        /*if ($addEmptyTemplate) {
606
            $templates = array('' => $plugin->get_lang('emptysite'));
607
        }*/
608
609
        $template = self::getConfig('vchamilo', 'default_template');
610
611
        if ($dirs) {
612
            /** @var Symfony\Component\Finder\SplFileInfo $dir */
613
            foreach ($dirs as $dir) {
614
                if (is_dir($dir->getPathname())) {
615
                    // A template is considered when a dump.sql exists.
616
                    if (file_exists($dir->getPathname().'/dump.sql')) {
617
                        $templateName = $dir->getRelativePathname();
618
                        if ($templateName == $template) {
619
                            $templateName .= ' (default)';
620
                        }
621
                        $templates[$dir->getRelativePathname()] = $templateName;
622
                    }
623
                }
624
            }
625
        }
626
627
        return $templates;
628
    }
629
630
    /**
631
    * this function set will map standard moodle API calls to chamilo
632
    * internal primitives. This avoids too many changes to do in imported
633
    * code
634
    */
635
    public static function getConfig($module, $key, $isplugin = true)
636
    {
637
        if ($isplugin) {
638
            $key = $module.'_'.$key;
639
        }
640
641
        $params = ['variable = ? AND subkey = ?' => [$key, $module]];
642
        $result = api_get_settings_params_simple($params);
643
        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...
644
            return $result['selected_value'];
645
        }
646
647
        return false;
648
    }
649
650
    /**
651
     * @param stdClass $vchamilo
652
     * @param string $template
653
     */
654
    public static function loadFilesFromTemplate($vchamilo, $template)
655
    {
656
        global $_configuration;
657
658
        // Make template directory (files and SQL).
659
        $separator = DIRECTORY_SEPARATOR;
660
        $templateDir = $_configuration['root_sys'].'plugin'.$separator.'vchamilo'.$separator.'templates'.$separator.$template;
661
        $vchamilo->virtual = true;
662
        $coursePath = self::getConfig('vchamilo', 'course_real_root').$separator.$vchamilo->slug;
0 ignored issues
show
Bug introduced by
Are you sure self::getConfig('vchamilo', 'course_real_root') of type false 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

662
        $coursePath = /** @scrutinizer ignore-type */ self::getConfig('vchamilo', 'course_real_root').$separator.$vchamilo->slug;
Loading history...
663
        $homePath = self::getConfig('vchamilo', 'home_real_root').$separator.$vchamilo->slug;
0 ignored issues
show
Bug introduced by
Are you sure self::getConfig('vchamilo', 'home_real_root') of type false 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

663
        $homePath = /** @scrutinizer ignore-type */ self::getConfig('vchamilo', 'home_real_root').$separator.$vchamilo->slug;
Loading history...
664
        $archivePath = self::getConfig('vchamilo', 'archive_real_root').$separator.$vchamilo->slug;
0 ignored issues
show
Bug introduced by
Are you sure self::getConfig('vchamilo', 'archive_real_root') of type false 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

664
        $archivePath = /** @scrutinizer ignore-type */ self::getConfig('vchamilo', 'archive_real_root').$separator.$vchamilo->slug;
Loading history...
665
        $uploadPath = self::getConfig('vchamilo', 'upload_real_root').$separator.$vchamilo->slug;
0 ignored issues
show
Bug introduced by
Are you sure self::getConfig('vchamilo', 'upload_real_root') of type false 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

665
        $uploadPath = /** @scrutinizer ignore-type */ self::getConfig('vchamilo', 'upload_real_root').$separator.$vchamilo->slug;
Loading history...
666
667
        // get the protocol free hostname
668
        Display::addFlash(
669
            Display::return_message("Copying {$templateDir}/data/courses => $coursePath")
670
        );
671
672
        copyDirTo(
673
            self::chopLastSlash($templateDir.'/data/courses'),
674
            self::chopLastSlash($coursePath),
675
            false
676
        );
677
678
        Display::addFlash(
679
            Display::return_message("Copying {$templateDir}/data/archive => $archivePath")
680
        );
681
682
        copyDirTo(
683
            self::chopLastSlash($templateDir.'/data/archive'),
684
            self::chopLastSlash($archivePath),
685
            false
686
        );
687
688
        Display::addFlash(
689
            Display::return_message("Copying {$templateDir}/data/home => $homePath")
690
        );
691
692
        copyDirTo(
693
            self::chopLastSlash($templateDir.'/data/home'),
694
            self::chopLastSlash($homePath),
695
            false
696
        );
697
698
        // Upload
699
        Display::addFlash(
700
            Display::return_message("Copying {$templateDir}/data/upload => $uploadPath")
701
        );
702
703
        copyDirTo(
704
            self::chopLastSlash($templateDir.'/data/upload/'),
705
            self::chopLastSlash($uploadPath),
706
            false
707
        );
708
    }
709
710
    /**
711
     * @param string $path
712
     *
713
     * @return mixed
714
     */
715
    public static function chopLastSlash($path)
716
    {
717
        return preg_replace('/\/$/', '', $path);
718
    }
719
720
    /**
721
     * @param string $str
722
     */
723
    public static function ctrace($str)
724
    {
725
        error_log($str);
726
        Display::addFlash(Display::return_message($str, 'normal', false));
727
    }
728
729
    /**
730
     * @param $file
731
     * @param $component
732
     * @param bool $return
733
     * @return string
734
     */
735
    public static function requireJs($file, $component, $return = false)
736
    {
737
        global $_configuration, $htmlHeadXtra;
738
739
        if (preg_match('/^local_/', $component)) {
740
            $component = str_replace('local_', '', $component);
741
            $path = 'local/';
742
        } else {
743
            $path = 'plugin/';
744
        }
745
746
        // Secure the postslashing of the roots.
747
        $root_web = $_configuration['root_web'].'/';
748
        $root_web = preg_replace('#//$#', '/', $root_web);
749
750
        $str = '<script type="text/javascript" src="'.$root_web.$path.$component.'/js/'.$file.'"></script>'."\n";
751
        if ($return === 'head') {
752
            $htmlHeadXtra[] = $str;
753
        }
754
755
        if ($return) {
756
            return $str;
757
        }
758
        echo $str;
759
    }
760
761
    /**
762
     * @param $file
763
     * @param $component
764
     * @param bool $return
765
     * @return string
766
     */
767
    public static function requireCss($file, $component, $return = false)
768
    {
769
        global $_configuration, $htmlHeadXtra;
770
771
        if (preg_match('/^local_/', $component)) {
772
            $component = str_replace('local_', '', $component);
773
            $path = 'local/';
774
        } else {
775
            $path = 'plugin/';
776
        }
777
778
        // Secure the postslashing of the roots.
779
        $root_web = $_configuration['root_web'].'/';
780
        $root_web = preg_replace('#//$#', '/', $root_web);
781
782
        $str = '<link rel="stylesheet" type="text/css" href="'.$root_web.$path.$component.'/'.$file.'.css" />'."\n";
783
        if ($return === 'head') {
784
            $htmlHeadXtra[] = $str;
785
        }
786
        if ($return) {
787
            return $str;
788
        }
789
        echo $str;
790
    }
791
792
    /**
793
     * @param string $url
794
     * @return string
795
     */
796
    public static function getSlugFromUrl($url)
797
    {
798
        $slugify = new Slugify();
799
        $urlInfo = parse_url($url);
800
        if (isset($urlInfo['host'])) {
801
            $path = $urlInfo['path'] != '/' ? '_'.$urlInfo['path'] : '';
802
            return $slugify->slugify($urlInfo['host'].$path);
803
        }
804
805
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
806
    }
807
808
    /**
809
     * Check if all settings are complete
810
     */
811
    public static function checkSettings()
812
    {
813
        $enabled = self::getConfig('vchamilo', 'enable_virtualisation');
814
815
        if (empty($enabled)) {
816
            api_not_allowed(true, 'Plugin is not enabled');
817
        }
818
819
        global $virtualChamilo;
820
        if (!isset($virtualChamilo)) {
821
            api_not_allowed(
822
                true,
823
                'You have to edit the configuration.php. Please check the readme file.'
824
            );
825
        }
826
827
        $coursePath = self::getConfig('vchamilo', 'course_real_root');
828
        $homePath = self::getConfig('vchamilo', 'home_real_root');
829
        $archivePath = self::getConfig('vchamilo', 'archive_real_root');
830
        $uploadPath = self::getConfig('vchamilo', 'upload_real_root');
831
        $cmdSql = self::getConfig('vchamilo', 'cmd_mysql');
832
        $cmdMySql = self::getConfig('vchamilo', 'cmd_mysqldump');
833
834
        if (empty($coursePath) || empty($homePath) || empty($uploadPath) || empty($archivePath) || empty($cmdSql) || empty($cmdMySql)) {
835
            api_not_allowed(true, 'You have to complete all plugin settings.');
836
        }
837
838
        $separator = DIRECTORY_SEPARATOR;
839
        $templatePath = api_get_path(SYS_PATH).'plugin'.$separator.'vchamilo'.$separator.'templates';
840
841
        $paths = [
842
            $coursePath,
843
            $homePath,
844
            $archivePath,
845
            $uploadPath,
846
            $templatePath
847
        ];
848
849
        foreach ($paths as $path) {
850
            $path = trim($path);
851
            if (is_dir($path)) {
852
                if (!is_writable($path)) {
853
                    Display::addFlash(
854
                        Display::return_message("Directory must have writable permissions: '$path'", 'warning')
855
                    );
856
                };
857
            } else {
858
                Display::addFlash(
859
                    Display::return_message("Directory doesn't exist: '$path'", 'warning')
860
                );
861
            }
862
        }
863
    }
864
865
    /**
866
     * @param object $instance
867
     * @return bool|\Doctrine\DBAL\Connection
868
     */
869
    public static function getConnectionFromInstance($instance, $getManager = false)
870
    {
871
        $dbParams = [
872
            'driver' => 'pdo_mysql',
873
            'host' => $instance->db_host,
874
            'user' => $instance->db_user,
875
            'password' => $instance->db_password,
876
            //'dbname' => $instance->main_database,
877
            // Only relevant for pdo_sqlite, specifies the path to the SQLite database.
878
            //'path' => isset($_configuration['db_path']) ? $_configuration['db_path'] : '',
879
            // Only relevant for pdo_mysql, pdo_pgsql, and pdo_oci/oci8,
880
            //'port' => isset($_configuration['db_port']) ? $_configuration['db_port'] : '',
881
        ];
882
883
        if (!empty($instance->main_database)) {
884
            $dbParams['dbname'] = $instance->main_database;
885
        }
886
887
        try {
888
            $database = new \Database();
889
            $manager = $database->connect(
890
                $dbParams,
891
                api_get_configuration_value('root_sys'),
0 ignored issues
show
Bug introduced by
It seems like api_get_configuration_value('root_sys') can also be of type false; however, parameter $sysPath of Database::connect() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

891
                /** @scrutinizer ignore-type */ api_get_configuration_value('root_sys'),
Loading history...
892
                api_get_configuration_value('root_sys'),
0 ignored issues
show
Bug introduced by
It seems like api_get_configuration_value('root_sys') can also be of type false; however, parameter $entityRootPath of Database::connect() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

892
                /** @scrutinizer ignore-type */ api_get_configuration_value('root_sys'),
Loading history...
893
                false,
894
                true
895
            );
896
897
            if ($getManager) {
898
                return $manager;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $manager returns the type Doctrine\ORM\EntityManager which is incompatible with the documented return type boolean|Doctrine\DBAL\Connection.
Loading history...
899
            }
900
901
            return $manager->getConnection();
902
        } catch (Exception $e) {
903
            error_log($e->getMessage());
904
        }
905
906
        return false;
907
    }
908
909
    /**
910
     * @param $data
911
     */
912
    public static function addInstance($data)
913
    {
914
        if (isset($data->what)) {
915
            unset($data->what);
916
        }
917
        if (isset($data->submitbutton)) {
918
            unset($data->submitbutton);
919
        }
920
        if (isset($data->id)) {
921
            unset($data->id);
922
        }
923
        if (isset($data->vid)) {
924
            unset($data->vid);
925
        }
926
        if (isset($data->testconnection)) {
927
            unset($data->testconnection);
928
        }
929
        if (isset($data->testdatapath)) {
930
            unset($data->testdatapath);
931
        }
932
933
        $registeronly = $data->registeronly;
934
        unset($data->registeronly);
935
        $data->lastcron = 0;
936
        $data->lastcrongap = 0;
937
        $data->croncount = 0;
938
939
        if (isset($data->template) && !empty($data->template)) {
940
            $template = $data->template;
941
        } else {
942
            $template = '';
943
        }
944
945
        $mainDatabase = api_get_configuration_value('main_database');
946
947
        if ($mainDatabase == $data->main_database) {
948
            Display::addFlash(
949
                Display::return_message('You cannot use the same database as the chamilo master', 'error')
950
            );
951
952
            return;
953
        }
954
955
        $databaseName = $data->main_database;
956
        $data->main_database = '';
957
        $connection = self::getConnectionFromInstance($data);
958
        $data->main_database = $databaseName;
959
        if (!$connection) {
960
            Display::addFlash(
961
                Display::return_message(
962
                    'Cannot connect to database with params: '.print_r($data, 1),
963
                    'error'
964
                )
965
            );
966
            return;
967
        }
968
969
        $data->root_web = api_add_trailing_slash($data->root_web);
970
971
        $data->archive_url = api_add_trailing_slash($data->archive_url);
972
        $data->home_url = api_add_trailing_slash($data->home_url);
973
        $data->upload_url = api_add_trailing_slash($data->upload_url);
974
        $data->course_url = api_add_trailing_slash($data->course_url);
975
976
        if (substr($data->root_web, 0, 4) != 'http') {
977
            $data->root_web = api_get_protocol().'://'.$data->root_web;
978
        }
979
980
        self::ctrace('Registering: '.$data->root_web);
981
        $tablename = Database::get_main_table('vchamilo');
982
        $sql = "SELECT * FROM $tablename 
983
                WHERE root_web = '".Database::escape_string($data->root_web)."'";
984
        $result = Database::query($sql);
985
986
        if (Database::num_rows($result)) {
987
            Database::update($tablename, $data, ['root_web = ?' => $data->root_web]);
988
            $virtualInfo = Database::fetch_array($result);
989
            $slug = $virtualInfo['slug'];
990
        } else {
991
            $slug = $data->slug = self::getSlugFromUrl($data->root_web);
992
            if (empty($slug)) {
993
                Display::addFlash(
994
                    Display::return_message('Cannot create slug from url: '.$data->root_web, 'error')
995
                );
996
                return;
997
            }
998
            Database::insert($tablename, (array) $data);
999
        }
1000
1001
        if ($registeronly) {
1002
            // Stop it now.
1003
            self::ctrace('Registering only. out.');
1004
            self::redirect(api_get_path(WEB_PLUGIN_PATH).'vchamilo/views/manage.php');
1005
        }
1006
1007
        // or we continue with physical creation
1008
        self::createDirsFromSlug($slug);
1009
1010
        if (!$template) {
1011
            // Create empty database for install
1012
            self::ctrace("Creating database");
1013
            self::createDatabase($data);
1014
        } else {
1015
            // Deploy template database
1016
            self::ctrace("Creating databases from template '$template'");
1017
            self::createDatabase($data);
1018
            self::ctrace("Loading data template '$template'");
1019
            self::loadDbTemplate($data, $template);
1020
            self::ctrace("Coying files from template '$template'");
1021
            self::loadFilesFromTemplate($data, $template);
1022
        }
1023
1024
        // pluging in site name institution
1025
        $settingstable = $data->main_database.'.settings_current';
1026
        $accessurltable = $data->main_database.'.access_url';
1027
1028
        $sitename = Database::escape_string($data->sitename);
1029
        $institution = Database::escape_string($data->institution);
1030
1031
        $sqls[] = "UPDATE {$settingstable} SET selected_value = '{$sitename}' 
0 ignored issues
show
Comprehensibility Best Practice introduced by
$sqls was never initialized. Although not strictly required by PHP, it is generally a good practice to add $sqls = array(); before regardless.
Loading history...
1032
                   WHERE variable = 'siteName' AND category = 'Platform' ";
1033
1034
        $sqls[] = "UPDATE {$settingstable} SET selected_value = '{$institution}' 
1035
                   WHERE variable = 'institution' AND category = 'Platform' ";
1036
1037
        $sqls[] = "UPDATE {$accessurltable} SET url = '{$data->root_web}' WHERE id = '1' ";
1038
1039
        foreach ($sqls as $sql) {
1040
            Database::query($sql);
1041
        }
1042
1043
        self::ctrace("Finished");
1044
    }
1045
1046
    /**
1047
     * @param stdClass $data
1048
     * @param string $fromVersion
1049
     */
1050
    public static function importInstance($data, $fromVersion)
1051
    {
1052
        if (isset($data->what)) {
1053
            unset($data->what);
1054
        }
1055
        if (isset($data->submitbutton)) {
1056
            unset($data->submitbutton);
1057
        }
1058
        if (isset($data->id)) {
1059
            unset($data->id);
1060
        }
1061
        if (isset($data->vid)) {
1062
            unset($data->vid);
1063
        }
1064
        if (isset($data->testconnection)) {
1065
            unset($data->testconnection);
1066
        }
1067
        if (isset($data->testdatapath)) {
1068
            unset($data->testdatapath);
1069
        }
1070
1071
        $fromCoursePath = $data->course_path;
1072
        $fromHomePath = $data->home_path;
1073
        $fromUploadPath = $data->upload_path;
1074
1075
        unset($data->course_path);
1076
        unset($data->home_path);
1077
        unset($data->upload_path);
1078
1079
        $newDatabase = clone $data;
1080
        $newDatabase->main_database = $newDatabase->import_to_main_database;
1081
        $newDatabase->db_user = $newDatabase->import_to_db_user;
1082
        $newDatabase->db_password = $newDatabase->import_to_db_password;
1083
        $newDatabase->db_host = $newDatabase->import_to_db_host;
1084
1085
        unset($newDatabase->import_to_main_database);
1086
        unset($newDatabase->import_to_db_user);
1087
        unset($newDatabase->import_to_db_password);
1088
        unset($newDatabase->import_to_db_host);
1089
1090
        unset($data->import_to_main_database);
1091
        unset($data->import_to_db_user);
1092
        unset($data->import_to_db_password);
1093
        unset($data->import_to_db_host);
1094
1095
        $data->lastcron = 0;
1096
        $data->lastcrongap = 0;
1097
        $data->croncount = 0;
1098
1099
        $mainDatabase = api_get_configuration_value('main_database');
1100
1101
        if ($mainDatabase == $data->main_database) {
1102
            Display::addFlash(
1103
                Display::return_message('You cannot use the same database as the chamilo master', 'error')
1104
            );
1105
1106
            return false;
1107
        }
1108
1109
        self::ctrace('Registering: '.$data->root_web);
1110
1111
        $table = Database::get_main_table('vchamilo');
1112
        $sql = "SELECT * FROM $table 
1113
                WHERE root_web = '".Database::escape_string($data->root_web)."'";
1114
        $result = Database::query($sql);
1115
        $id = null;
1116
        if (Database::num_rows($result)) {
1117
            Display::addFlash(
1118
                Display::return_message('Instance was already added: '.$data->root_web, 'error')
1119
            );
1120
            return false;
1121
        } else {
1122
            /** @var EntityManager $em */
1123
            $em = self::getConnectionFromInstance($data, true);
1124
            if ($em) {
1125
                $connection = $em->getConnection();
1126
                $statement = $connection->query('SELECT * FROM settings_current');
1127
                $settings = $statement->fetchAll();
1128
                $settings = array_column(
1129
                    $settings,
1130
                    'selected_value',
1131
                    'variable'
1132
                );
1133
                $institution = $settings['Institution'];
1134
                $siteName = $settings['siteName'];
1135
                $newDatabase->sitename = $siteName;
1136
                $newDatabase->institution = $institution;
1137
                $slug = $newDatabase->slug = $data->slug = self::getSlugFromUrl($data->root_web);
1138
                $id = Database::insert($table, (array) $newDatabase);
1139
            }
1140
        }
1141
1142
        if (!$id) {
1143
            // Show data detail to help debug
1144
            //var_dump($data);
1145
            throw new Exception('New/Imported instance was not registered - edit '.__FILE__.' on line '.__LINE__.'to var_dump');
1146
        }
1147
1148
        if (empty($slug)) {
1149
            throw new Exception('Slug is empty');
1150
        }
1151
1152
        self::createDirsFromSlug($slug);
1153
        $databaseCreated = self::createDatabase($newDatabase);
1154
        if (!$databaseCreated) {
1155
            Display::addFlash(
1156
                Display::return_message('Error while creating a DB', 'error')
1157
            );
1158
            return false;
1159
        }
1160
1161
        $coursePath = self::getConfig('vchamilo', 'course_real_root').'/'.$slug;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $slug does not seem to be defined for all execution paths leading up to this point.
Loading history...
1162
        $homePath = self::getConfig('vchamilo', 'home_real_root').'/'.$slug;
1163
        $uploadPath = self::getConfig('vchamilo', 'upload_real_root').'/'.$slug;
1164
1165
        $dumpFile = api_get_path(SYS_ARCHIVE_PATH).uniqid($data->main_database.'_dump_', true).'.sql';
1166
        self::ctrace('Create backup from "'.$data->main_database.'" here: '.$dumpFile.' ');
1167
        self::backupDatabase($data, $dumpFile);
1168
1169
        $sqlcmd = self::getDatabaseDumpCmd($newDatabase);
1170
        $sqlcmd = str_replace('%DATABASE%', $newDatabase->main_database, $sqlcmd);
1171
        $import = $sqlcmd.$dumpFile;
1172
1173
        // Execute the command.
1174
        if (!defined('CLI_SCRIPT')) {
1175
            putenv('LANG=en_US.utf-8');
1176
        }
1177
1178
        // ensure utf8 is correctly handled by php exec()
1179
        // @see http://stackoverflow.com/questions/10028925/call-a-program-via-shell-exec-with-utf-8-text-input
1180
        $result = exec($import, $output, $return);
1181
1182
        self::ctrace('Restore backup here "'.$newDatabase->main_database.'" : <br />'.$import.' ');
1183
        self::ctrace($result);
1184
1185
        $command = new \Chash\Command\Installation\UpgradeDatabaseCommand();
1186
        // Creates the helper set
1187
        $helperSet = \Doctrine\ORM\Tools\Console\ConsoleRunner::createHelperSet($em);
1188
1189
        $helpers = [
1190
            'configuration' => new Chash\Helpers\ConfigurationHelper(),
1191
            'dialog' => new \Symfony\Component\Console\Helper\QuestionHelper(),
1192
        ];
1193
1194
        foreach ($helpers as $name => $helper) {
1195
            $helperSet->set($helper, $name);
1196
        }
1197
1198
        $command->setHelperSet($helperSet);
1199
1200
        $tmpFile = tmpfile();
1201
        $outputStream = new \Symfony\Component\Console\Output\BufferedOutput($tmpFile);
1202
1203
        $arguments = [
1204
            'from-version' => $fromVersion, // @todo change value
1205
            'to-version' => '1.11.x',
1206
            'host' => $newDatabase->db_host,
1207
            'username' => $newDatabase->db_user,
1208
            'password' => $newDatabase->db_password,
1209
            'db_name' => $newDatabase->main_database,
1210
            'root_sys' => api_get_configuration_value('root_sys')
1211
        ];
1212
1213
        $input = new ArrayInput($arguments);
1214
        $command->run($input, $outputStream);
1215
1216
        error_log($outputStream->fetch());
1217
1218
        if (file_exists($dumpFile)) {
1219
            unlink($dumpFile);
1220
        }
1221
1222
        // Course
1223
        self::ctrace("Copy from '$fromCoursePath' to backup '$coursePath' ");
1224
        copyDirTo(
1225
            self::chopLastSlash($fromCoursePath),
1226
            self::chopLastSlash($coursePath),
1227
            false
1228
        );
1229
1230
        // Home
1231
        self::ctrace("Copy from '$fromHomePath' to backup '$homePath' ");
1232
        copyDirTo(
1233
            self::chopLastSlash($fromHomePath),
1234
            self::chopLastSlash($homePath),
1235
            false
1236
        );
1237
1238
        // Upload
1239
        self::ctrace("Copy from '$fromUploadPath' to backup '$uploadPath' ");
1240
        copyDirTo(
1241
            self::chopLastSlash($fromUploadPath),
1242
            self::chopLastSlash($uploadPath),
1243
            false
1244
        );
1245
1246
        self::ctrace("Finished");
1247
    }
1248
1249
    /**
1250
     * @param string $slug
1251
     *
1252
     * @return string
1253
     */
1254
    public static function createDirsFromSlug($slug)
1255
    {
1256
        // We continue with physical creation
1257
1258
        // Create course directory for operations.
1259
        // this is very important here (DO NOT USE api_get_path() !!) because storage may be remotely located
1260
        $absAlternateCourse = self::getConfig('vchamilo', 'course_real_root');
1261
        $courseDir = $absAlternateCourse.'/'.$slug;
0 ignored issues
show
Bug introduced by
Are you sure $absAlternateCourse of type false 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

1261
        $courseDir = /** @scrutinizer ignore-type */ $absAlternateCourse.'/'.$slug;
Loading history...
1262
1263
        if (!is_dir($courseDir)) {
1264
            self::ctrace("Creating physical course dir in $courseDir");
1265
            mkdir($courseDir, 0777, true);
1266
            // initiate default index
1267
            $indexFile = $courseDir.'/index.html';
1268
            if ($indexFile) {
1269
                file_put_contents($indexFile, self::getDefaultCourseIndexFragment());
1270
            }
1271
1272
            $htaccessFile = $courseDir.'/.htaccess';
1273
            if ($htaccessFile) {
1274
                file_put_contents($htaccessFile, self::getHtaccessFragment($slug));
1275
            }
1276
        }
1277
1278
        $absAlternateHome = self::getConfig('vchamilo', 'home_real_root');
1279
        $absAlternateArchive = self::getConfig('vchamilo', 'archive_real_root');
1280
        $absAlternateUpload = self::getConfig('vchamilo', 'upload_real_root');
1281
1282
        // absalternatehome is a vchamilo config setting that tells where the
1283
        // real physical storage for home pages are.
1284
        $homeDir = $absAlternateHome.'/'.$slug;
0 ignored issues
show
Bug introduced by
Are you sure $absAlternateHome of type false 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

1284
        $homeDir = /** @scrutinizer ignore-type */ $absAlternateHome.'/'.$slug;
Loading history...
1285
        $archiveDir = $absAlternateArchive.'/'.$slug;
0 ignored issues
show
Bug introduced by
Are you sure $absAlternateArchive of type false 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

1285
        $archiveDir = /** @scrutinizer ignore-type */ $absAlternateArchive.'/'.$slug;
Loading history...
1286
        $uploadDir = $absAlternateUpload.'/'.$slug;
0 ignored issues
show
Bug introduced by
Are you sure $absAlternateUpload of type false 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

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