upgrade.php 65.8 KB
Newer Older
1
2
3
4
<?php
/**
 *
 * @package    mahara
5
 * @subpackage core
6
 * @author     Catalyst IT Ltd
7
8
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later
 * @copyright  For copyright information on Mahara, please see the README file distributed with this software.
9
10
11
12
13
14
15
16
 *
 */

defined('INTERNAL') || die();

/**
 * Class to use for installation exceptions
 */
17
class InstallationException extends SystemException {}
18

19
require_once('ddl.php');
20
21

/**
22
23
 * This function checks core and plugins for which need to be upgraded/installed
 *
24
25
26
27
 * Note: This function is sometimes executed during upgrades from
 * ancient databases.  Avoid rash assumptions about what's installed
 * or these upgrades may fail.
 *
28
29
 * @param string $name The name of the plugin to check. If no name is specified,
 *                     all plugins are checked.
30
31
32
 * @return mixed If a name is specified, an object will be returned with upgrade data
 *                     about the requested component (which can be "core", "local", or a plugin).
 *                     If the component desn't need to be updated, an empty array will be returned.
33
34
35
 *               If no name is specified, an array of such objects will be returned.
 *                     It will also include an array key "settings", which will be an array
 *                     that may contain metadata about the upgrade/install process.
36
 */
37
function check_upgrades($name=null) {
38

39
40
41
    $pluginstocheck = plugin_types();

    $toupgrade = array();
42
    $settings = array();
43
44
    $toupgradecount = 0;
    $newinstallcount = 0;
45
    $installing = false;
46
    $newinstalls = array();
47
48
49
50
51
52

    require('version.php');
    // check core first...
    if (empty($name) || $name == 'core') {
        try {
            $coreversion = get_config('version');
Elliot Pahl's avatar
Elliot Pahl committed
53
        }
54
55
56
        catch (Exception $e) {
            $coreversion = 0;
        }
57
        $corerelease = get_config('release');
58
59
60
61
        $core = new stdClass();
        $core->to = $config->version;
        $core->torelease = $config->release;
        $core->toseries = $config->series;
62
        $toupgrade['core'] = $core;
63
        if (empty($coreversion)) {
64
            if (is_mysql()) { // Show a more informative error message if using mysql with skip-innodb
65
                // In MySQL 5.6.x, we run the command 'SHOW ENGINES' to check if InnoDB is enabled or not
66
                global $db;
67
68
69
70
71
72
73
74
75
76
77
                $result = $db->Execute("SHOW ENGINES");
                $hasinnodb = false;
                while (!$result->EOF) {
                    if ($result->fields['Engine'] == 'InnoDB' && ($result->fields['Support'] == 'YES' || $result->fields['Support'] == 'DEFAULT')) {
                        $hasinnodb = true;
                        break;
                    }
                    $result->MoveNext();
                }

                if (!$hasinnodb) {
78
79
80
                    throw new ConfigSanityException("Mahara requires InnoDB tables.  Please ensure InnoDB tables are enabled in your MySQL server.");
                }
            }
81
82
            $core->install = true;
            $installing = true;
Elliot Pahl's avatar
Elliot Pahl committed
83
        }
84
        else if ($config->version > $coreversion) {
Elliot Pahl's avatar
Elliot Pahl committed
85
            if (isset($config->minupgradefrom) && isset($config->minupgraderelease)
86
                && $coreversion < $config->minupgradefrom) {
87
                throw new ConfigSanityException("Must upgrade to $config->minupgradefrom "
88
89
                                          . "($config->minupgraderelease) first "
                                          . " (you have $coreversion ($corerelease)");
90
            }
91
            $toupgradecount ++;
92
93
            $core->upgrade = true;
            $core->from = $coreversion;
94
            $core->fromrelease = $corerelease;
95
        }
96
97
98
99
100
101
102
        else if ($config->version < $coreversion) {
            if (get_config('productionmode')) {
                throw new ConfigSanityException("Database version of Mahara $corerelease ($coreversion) is newer "
                                            . "than files version $config->release ($config->version). "
                                            . "Please make sure you have the correct Mahara files in place.");
            }
            else {
103
104
105
                if (!defined('SITEOUTOFSYNC')) {
                    define('SITEOUTOFSYNC', 'core');
                }
106
107
            }
        }
108
109
110
111
        else {
            // Core doesn't need to be upgraded. Remove it from the list!
            unset($toupgrade['core']);
        }
112
113
    }

114
    // If we were just checking if the core needed to be upgraded, we can stop here
115
116
117
118
    if ($name == 'core') {
        return $toupgrade['core'];
    }

119
120
121
122
123
124
125
126
    if (!$installing && (empty($name) || $name == 'local')) {
        $localversion = get_config('localversion');
        $localrelease = get_config('localrelease');
        if (is_null($localversion)) {
            $localversion = 0;
            $localrelease = 0;
        }

127
        $config = new stdClass();
128
129
130
        require(get_config('docroot') . 'local/version.php');

        if ($config->version > $localversion) {
131
            $toupgradecount ++;
132
133
134
135
136
137
138
139
140
141
142
143
144
145
            $toupgrade['local'] = (object) array(
                'upgrade'     => true,
                'from'        => $localversion,
                'fromrelease' => $localrelease,
                'to'          => $config->version,
                'torelease'   => $config->release,
            );
        }

        if ($name == 'local') {
            return $toupgrade['local'];
        }
    }

146
147
    $plugins = array();
    if (!empty($name)) {
148
149
150
151
152
153
        try {
            $bits = explode('.', $name);
            $pt = $bits[0];
            $pn = $bits[1];
            $pp = null;
            if ($pt == 'blocktype' && strpos($pn, '/') !== false) {
154
                $bits = explode('/', $pn);
155
156
157
158
159
160
                $pp = get_config('docroot') . 'artefact/' . $bits[0]  . '/blocktype/' . $bits[1];
            }
            validate_plugin($pt, $pn, $pp);
            $plugins[] = explode('.', $name);
        }
        catch (InstallationException $_e) {
161
            log_warn(get_string('pluginnotinstallable', 'mahara', $pt, $pn) . $_e->GetMessage());
162
        }
163
164
165
166
167
    }
    else {
        foreach ($pluginstocheck as $plugin) {
            $dirhandle = opendir(get_config('docroot') . $plugin);
            while (false !== ($dir = readdir($dirhandle))) {
168
                if (strpos($dir, '.') === 0 or 'CVS' == $dir) {
169
170
                    continue;
                }
171
172
                $plugin_dir = get_config('docroot') . $plugin . '/' . $dir;
                if (!is_dir($plugin_dir)) {
173
174
                    continue;
                }
175
176
177
178
179
                try {
                    validate_plugin($plugin, $dir);
                    $plugins[] = array($plugin, $dir);
                }
                catch (InstallationException $_e) {
180
                    log_warn(get_string('pluginnotinstallable', 'mahara', $plugin, $dir) . $_e->GetMessage(), true , false);
181
182
183
                }

                if ($plugin == 'artefact') { // go check it for blocks as well
184
                    $btlocation = $plugin_dir . '/blocktype';
185
                    if (!is_dir($btlocation)) {
186
                        continue;
Elliot Pahl's avatar
Elliot Pahl committed
187
                    }
188
189
                    $btdirhandle = opendir($btlocation);
                    while (false !== ($btdir = readdir($btdirhandle))) {
190
                        if (strpos($btdir, '.') === 0 or 'CVS' == $btdir) {
191
192
                            continue;
                        }
193
                        if (!is_dir(get_config('docroot') . $plugin . '/' . $dir . '/blocktype/' . $btdir)) {
194
195
196
                            continue;
                        }
                        $plugins[] = array('blocktype', $dir . '/' . $btdir);
197
198
                    }
                }
199
200
201
            }
        }
    }
202
    $outofsyncplugins = array();
203
204
205
206
207
208
    foreach ($plugins as $plugin) {
        $plugintype = $plugin[0];
        $pluginname = $plugin[1];
        $pluginpath = "$plugin[0]/$plugin[1]";
        $pluginkey  = "$plugin[0].$plugin[1]";

209
210
211
212
213
214
        if ($plugintype == 'blocktype' && strpos($pluginname, '/') !== false) {
            // sigh.. we're a bit special...
            $bits = explode('/', $pluginname);
            $pluginpath = 'artefact/' . $bits[0] . '/blocktype/' . $bits[1];
        }

215
216
217
        // Don't try to get the plugin info if we are installing - it will
        // definitely fail
        $pluginversion = 0;
218
219
220
221
222
223
224
225
226
        if (!$installing && table_exists(new XMLDBTable($plugintype . '_installed'))) {
            if ($plugintype == 'blocktype' && strpos($pluginname, '/')) {
                $bits = explode('/', $pluginname);
                $installed = get_record('blocktype_installed', 'name', $bits[1], 'artefactplugin', $bits[0]);
            }
            else {
                $installed = get_record($plugintype . '_installed', 'name', $pluginname);
            }
            if ($installed) {
227
228
229
                $pluginversion = $installed->version;
                $pluginrelease =  $installed->release;
            }
230
        }
231

232
        $config = new stdClass();
233
        require(get_config('docroot') . $pluginpath . '/version.php');
234

235
236
237
238
239
        $classname = generate_class_name($plugintype, $pluginname);
        safe_require($plugintype, $pluginname);
        // Check if there is a displayname
        $plugindisplayname = call_static_method($classname, 'get_plugin_display_name');

240
        if (empty($pluginversion)) {
241
            $newinstall = false;
242
            if (empty($installing) && $pluginkey != $name) {
243
                $newinstall = true;
244
            }
245
            $plugininfo = new stdClass();
246
247
248
            $plugininfo->install = true;
            $plugininfo->to = $config->version;
            $plugininfo->torelease = $config->release;
249
250
251
252
253
254
            if (property_exists($config, 'requires_config')) {
                $plugininfo->requires_config = $config->requires_config;
            }
            if (property_exists($config, 'requires_parent')) {
                $plugininfo->requires_parent = $config->requires_parent;
            }
255
            $plugininfo->displayname = $plugindisplayname;
256

257
258
259
260
261
262
263
264
265
            try {
                $classname::sanity_check();
            }
            catch (InstallationException $exc) {
                $plugininfo->to = get_string('notinstalled', 'admin');
                $plugininfo->torelease = get_string('notinstalled', 'admin');
                $plugininfo->errormsg = $exc->getMessage();
            }

266
267
268
269
270
271
272
273
274
275
            if ($newinstall) {
                $plugininfo->from = get_string('notinstalled', 'admin');
                $plugininfo->fromrelease = get_string('notinstalled', 'admin');
                $plugininfo->newinstall = true;
                $newinstallcount ++;
                $newinstalls[$pluginkey] = $plugininfo;
            }
            else {
                $toupgrade[$pluginkey] = $plugininfo;
            }
276
277
        }
        else if ($config->version > $pluginversion) {
278
279
280
281
282
            if (isset($config->minupgradefrom) && isset($config->minupgraderelease)
                && $pluginversion < $config->minupgradefrom) {
                throw new ConfigSanityException("Must upgrade to $config->minupgradefrom "
                                          . " ($config->minupgraderelease) first "
                                          . " (you have $pluginversion ($pluginrelease))");
283
            }
284
            $toupgradecount++;
285
            $plugininfo = new stdClass();
286
287
288
289
290
            $plugininfo->upgrade = true;
            $plugininfo->from = $pluginversion;
            $plugininfo->fromrelease = $pluginrelease;
            $plugininfo->to = $config->version;
            $plugininfo->torelease = $config->release;
291
292
293
294
295
296
            if (property_exists($config, 'requires_config')) {
                $plugininfo->requires_config = $config->requires_config;
            }
            if (property_exists($config, 'requires_parent')) {
                $plugininfo->requires_parent = $config->requires_parent;
            }
297
            $plugininfo->displayname = $plugindisplayname;
298
299
300
301
302
303
304
305
306
307
308
309
310

            try {
                $classname::sanity_check();
            }
            catch (InstallationException $exc) {
                $plugininfo->to = $config->version;
                $plugininfo->torelease = $pluginrelease;
                $plugininfo->errormsg = $exc->getMessage();
                $toupgrade[$pluginkey] = $plugininfo;

                continue;
            }

311
312
            $toupgrade[$pluginkey] = $plugininfo;
        }
313
        else if ($config->version < $pluginversion) {
314
            $plugindisplayname = !empty($plugindisplayname) ? $plugindisplayname : (!empty($config->name) ? $config->name : $pluginpath);
315
316
317
318
319
320
321
            if (get_config('productionmode')) {
                throw new ConfigSanityException("Database version of Mahara plugin " . $plugindisplayname . " "
                                            . $pluginrelease . " (" . $pluginversion . ") is newer "
                                            . "than files version " . $config->release . " (" . $config->version . "). "
                                            . "Please make sure you have the correct Mahara plugin files in place.");
            }
            else {
322
                $outofsyncplugins[] = $plugindisplayname;
323
324
            }
        }
325
    }
326
    if (!empty($outofsyncplugins)) {
327
328
329
        if (!defined('SITEOUTOFSYNC')) {
            define('SITEOUTOFSYNC', implode(', ', $outofsyncplugins));
        }
330
    }
331
    // if we've just asked for one, don't return an array...
332
    if (!empty($name)) {
333
        if (count($toupgrade) == 1) {
334
            $upgrade = new stdClass();
335
336
337
338
339
340
341
342
            $upgrade->name = $name;
            foreach ((array)$toupgrade[$name] as $key => $value) {
                $upgrade->{$key} = $value;
            }
            return $upgrade;
        }
        else {
            return array();
343
344
        }
    }
345
346

    // If we get here, it's because we have an array of objects to return
347
    uksort($toupgrade, 'sort_upgrades');
348
    $settings['newinstallcount'] = $newinstallcount;
349
    $settings['newinstalls'] = $newinstalls;
350
351
    $settings['toupgradecount'] = $toupgradecount;
    $toupgrade['settings'] = $settings;
352
353
354
    return $toupgrade;
}

355
356
357
358
359
/**
 * Upgrades the core system to given upgrade version.
 *
 * @param object $upgrade   The version to upgrade to
 * @return bool             Whether the upgrade succeeded or not
360
 * @throws SQLException     If the upgrade failed due to a database error
361
 */
362
363
364
function upgrade_core($upgrade) {
    global $db;

365
    $location = get_config('libroot') . 'db/';
Penny Leach's avatar
Penny Leach committed
366
367

    db_begin();
368
369

    if (!empty($upgrade->install)) {
370
        install_from_xmldb_file($location . 'install.xml');
371
372
373
    }
    else {
        require_once($location . 'upgrade.php');
Penny Leach's avatar
Penny Leach committed
374
        xmldb_core_upgrade($upgrade->from);
375
376
    }

Penny Leach's avatar
Penny Leach committed
377
378
    set_config('version', $upgrade->to);
    set_config('release', $upgrade->torelease);
379
    set_config('series', $upgrade->toseries);
380
    bump_cache_version();
381
    cron_check_for_updates();
Elliot Pahl's avatar
Elliot Pahl committed
382

383
    if (!empty($upgrade->install)) {
Penny Leach's avatar
Penny Leach committed
384
        core_postinst();
385
    }
386

Penny Leach's avatar
Penny Leach committed
387
388
    db_commit();
    return true;
389
390
}

391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
/**
 * Upgrades local customisations.
 *
 * @param object $upgrade   The version to upgrade to
 * @return bool             Whether the upgrade succeeded or not
 * @throws SQLException     If the upgrade failed due to a database error
 */
function upgrade_local($upgrade) {
    db_begin();

    require_once(get_config('docroot') . 'local/upgrade.php');
    xmldb_local_upgrade($upgrade->from);

    set_config('localversion', $upgrade->to);
    set_config('localrelease', $upgrade->torelease);
406
    bump_cache_version();
407
408
409
410
411

    db_commit();
    return true;
}

412
413
414
/**
 * Upgrades the plugin to a new version
 *
415
416
417
418
 * Note: This function is sometimes executed during upgrades from
 * ancient databases.  Avoid rash assumptions about what's installed
 * or these upgrades may fail.
 *
419
420
 * @param object $upgrade   Information about the plugin to upgrade
 * @return bool             Whether the upgrade succeeded or not
421
 * @throws SQLException     If the upgrade failed due to a database error
422
 */
423
424
425
426
427
428
429
430
function upgrade_plugin($upgrade) {
    global $db;

    $plugintype = '';
    $pluginname = '';

    list($plugintype, $pluginname) = explode('.', $upgrade->name);

431
432
    if ($plugintype == 'blocktype' && strpos($pluginname, '/') !== false) {
        list($artefactplugin, $blocktypename) = explode('/', $pluginname);
433
434
435
436
437
438
        $location = get_config('docroot') . 'artefact/' . $artefactplugin . '/blocktype/' . $blocktypename . '/db/';
        $function = 'xmldb_' . $plugintype . '_' . $blocktypename . '_upgrade';
    }
    else {
        $location = get_config('docroot') . $plugintype . '/' . $pluginname . '/db/';
        $function = 'xmldb_' . $plugintype . '_' . $pluginname . '_upgrade';
Elliot Pahl's avatar
Elliot Pahl committed
439
    }
440

441
    db_begin();
442
443
444

    if (!empty($upgrade->install)) {
        if (is_readable($location . 'install.xml')) {
445
            install_from_xmldb_file($location . 'install.xml');
446
447
448
449
450
        }
    }
    else {
        if (is_readable($location .  'upgrade.php')) {
            require_once($location . 'upgrade.php');
451
452
453
            if (!$function($upgrade->from)) {
                throw new InstallationException("Failed to run " . $function . " (check logs for errors)");
            }
454
455
456
        }
    }

457
    $installed = new stdClass();
458
459
460
    $installed->name = $pluginname;
    $installed->version = $upgrade->to;
    $installed->release = $upgrade->torelease;
461
462
463
464
465
466
467
468
    if ($plugintype == 'blocktype') {
        if (!empty($blocktypename)) {
            $installed->name = $blocktypename;
        }
        if (!empty($artefactplugin)) { // blocks come from artefactplugins.
            $installed->artefactplugin = $artefactplugin;
        }
    }
469
470
471
472
473
474
    if (property_exists($upgrade, 'requires_config')) {
        $installed->requires_config = $upgrade->requires_config;
    }
    if (property_exists($upgrade, 'requires_parent')) {
        $installed->requires_parent = $upgrade->requires_parent;
    }
475
    $installtable = $plugintype . '_installed';
476
477
478

    if (!empty($upgrade->install)) {
        insert_record($installtable,$installed);
Elliot Pahl's avatar
Elliot Pahl committed
479
    }
480
481
482
    else {
        update_record($installtable, $installed, 'name');
    }
483
    bump_cache_version();
484
485

    // postinst stuff...
Nigel McNie's avatar
Nigel McNie committed
486
    safe_require($plugintype, $pluginname);
487
    $pcname = generate_class_name($plugintype, $installed->name);
488
489
490
491
492
493
494
495
496
497
498

    if ($crons = call_static_method($pcname, 'get_cron')) {
        foreach ($crons as $cron) {
            $cron = (object)$cron;
            if (empty($cron->callfunction)) {
                throw new InstallationException("cron for $pcname didn't supply function name");
            }
            if (!is_callable(array($pcname, $cron->callfunction))) {
                throw new InstallationException("cron $cron->callfunction for $pcname supplied but wasn't callable");
            }
            $new = false;
499
            $table = $plugintype . '_cron';
500
501
502
            if (!empty($upgrade->install)) {
                $new = true;
            }
503
            else if (!record_exists($table, 'plugin', $pluginname, 'callfunction', $cron->callfunction)) {
504
505
506
507
                $new = true;
            }
            $cron->plugin = $pluginname;
            if (!empty($new)) {
508
                insert_record($table, $cron);
509
510
            }
            else {
511
                update_record($table, $cron, array('plugin', 'callfunction'));
512
513
514
            }
        }
    }
Elliot Pahl's avatar
Elliot Pahl committed
515

516
517
518
519
    if ($events = call_static_method($pcname, 'get_event_subscriptions')) {
        foreach ($events as $event) {
            $event = (object)$event;

520
            if (!record_exists('event_type', 'name', $event->event)) {
521
522
523
524
525
526
527
528
529
                throw new InstallationException("event $event->event for $pcname doesn't exist!");
            }
            if (empty($event->callfunction)) {
                throw new InstallationException("event $event->event for $pcname didn't supply function name");
            }
            if (!is_callable(array($pcname, $event->callfunction))) {
                throw new InstallationException("event $event->event with function $event->callfunction for $pcname supplied but wasn't callable");
            }
            $exists = false;
530
            $table = $plugintype . '_event_subscription';
531
            $block = blocktype_namespaced_to_single($pluginname);
532
            if (empty($upgrade->install)) {
533
                $exists = get_record($table, 'plugin' , $block, 'event', $event->event);
534
            }
535
            $event->plugin = $block;
536
            if (empty($exists)) {
537
                insert_record($table, $event);
538
539
            }
            else {
540
                update_record($table, $event, array('id' => $exists->id));
541
542
543
544
            }
        }
    }

545
546
547
548
549
550
    if ($activities = call_static_method($pcname, 'get_activity_types')) {
        foreach ($activities as $activity) {
            $classname = 'ActivityType' . ucfirst($plugintype) . ucfirst($pluginname) . ucfirst($activity->name);
            if (!class_exists($classname)) {
                throw new InstallationException(get_string('classmissing', 'error',  $classname, $pluginname, $plugintype));
            }
551
552
553
554
555
556
557
558
559
560
561
562
            // Add activity_type if it doesn't exist
            if (!get_record('activity_type', 'name', $activity->name, 'plugintype', $plugintype, 'pluginname', $pluginname)) {
                $activity->plugintype = $plugintype;
                $activity->pluginname = $pluginname;
                $activity->defaultmethod = get_config('defaultnotificationmethod') ? get_config('defaultnotificationmethod') : $activity->defaultmethod;
                $where = (object) array(
                    'name'       => $activity->name,
                    'plugintype' => $plugintype,
                    'pluginname' => $pluginname,
                );
                ensure_record_exists('activity_type', $where, $activity);
            }
563
564
565
        }
    }

566
    // install artefact types
567
    if ($plugintype == 'artefact') {
568
569
570
        if (!is_callable(array($pcname, 'get_artefact_types'))) {
            throw new InstallationException("Artefact plugin $pcname must implement get_artefact_types and doesn't");
        }
571
572
573
        $types = call_static_method($pcname, 'get_artefact_types');
        $ph = array();
        if (is_array($types)) {
574
575
576
577
578
579
580
581
582
583
            // Check for missing plugins - don't try to remove their data.
            // Bugs 505732 and 1287344.
            $used_types = get_records_sql_assoc("SELECT t.name, count(a.id) ct, t.plugin FROM {artefact_installed_type} t
                    LEFT JOIN {artefact} a ON t.name = a.artefacttype
                    GROUP BY t.name
                    HAVING count(a.id) > 0 AND plugin = '$pluginname'");
            if ($used_types === FALSE) {
                $used_types = array();
            }

584
585
586
            foreach ($types as $type) {
                $ph[] = '?';
                if (!record_exists('artefact_installed_type', 'plugin', $pluginname, 'name', $type)) {
587
                    $t = new stdClass();
588
589
590
591
                    $t->name = $type;
                    $t->plugin = $pluginname;
                    insert_record('artefact_installed_type',$t);
                }
592
593
594
                if (isset($used_types[$type])) {
                    unset($used_types[$type]);
                }
595
            }
596
597
598
599
600
601
602

            foreach ($used_types as $type) {
                $ph[] = '?';
            }

            $used_types = array_keys($used_types);

603
604
            $select = '(plugin = ? AND name NOT IN (' . implode(',', $ph) . '))';
            delete_records_select('artefact_installed_type', $select,
605
606
607
608
                    array_merge(array($pluginname),$types,$used_types));
            if (!empty($used_types)) {
                log_warn('Plugin for artefact type(s) "' . implode('", "', $used_types) . '" has gone away', true, false);
            }
609
610
        }
    }
Elliot Pahl's avatar
Elliot Pahl committed
611

612
613
614
    // install blocktype categories.
    if ($plugintype == 'blocktype' && get_config('installed')) {
        install_blocktype_categories_for_plugin($pluginname);
615
        install_blocktype_viewtypes_for_plugin($pluginname);
616
617
    }

618
619
    $prevversion = (empty($upgrade->install)) ? $upgrade->from : 0;
    call_static_method($pcname, 'postinst', $prevversion);
Elliot Pahl's avatar
Elliot Pahl committed
620

621
622
    db_commit();
    return true;
623
624
625
}

function core_postinst() {
626
    // Attempt to create session directories
627
    $sessionpath = get_config('sessionpath');
628
    $status = Session::create_directory_levels($sessionpath);
629

630
    $now = db_format_timestamp(time());
631
632
633
    // Set default search plugin
    set_config('searchplugin', 'internal');

634
    set_config('lang', 'en.utf8');
635
    set_config('installation_key', get_random_key());
636
    set_config('installation_time', $now);
637
    set_config('stats_installation_time', $now);
638
    set_config('passwordpolicy', '8_ulns');
639

640
641
642
643
644
645
646
    // Pre-define SMTP settings
    set_config('smtphosts', '');
    set_config('smtpport', '');
    set_config('smtpuser', '');
    set_config('smtppass', '');
    set_config('smtpsecure', '');

647
648
649
650
651
652
653
654
655
    // XMLDB adds a table's keys immediately after creating the table.  Some
    // foreign keys therefore cannot be created during the XMLDB installation,
    // because they refer to tables created later in the installation.  These
    // missing keys can be created now that all the core tables exist.
    $table = new XMLDBTable('usr');
    $key = new XMLDBKey('profileiconfk');
    $key->setAttributes(XMLDB_KEY_FOREIGN, array('profileicon'), 'artefact', array('id'));
    add_key($table, $key);

656
657
658
659
660
    $table = new XMLDBTable('institution');
    $key = new XMLDBKey('logofk');
    $key->setAttributes(XMLDB_KEY_FOREIGN, array('logo'), 'artefact', array('id'));
    add_key($table, $key);

661
    // PostgreSQL supports indexes over functions of columns, MySQL does not.
662
    // We make use if this if we can
663
    if (is_postgres()) {
664
        // Improve the username index
665
666
        execute_sql('DROP INDEX {usr_use_uix}');
        execute_sql('CREATE UNIQUE INDEX {usr_use_uix} ON {usr}(LOWER(username))');
667

668
669
670
671
672
673
674
675
676
        // Add user search indexes
        // Postgres only.  We could create non-lowercased indexes in MySQL, but
        // they would not be useful, and would require a change to varchar columns.
        execute_sql('CREATE INDEX {usr_fir_ix} ON {usr}(LOWER(firstname))');
        execute_sql('CREATE INDEX {usr_las_ix} ON {usr}(LOWER(lastname))');
        execute_sql('CREATE INDEX {usr_pre_ix} ON {usr}(LOWER(preferredname))');
        execute_sql('CREATE INDEX {usr_ema_ix} ON {usr}(LOWER(email))');
        execute_sql('CREATE INDEX {usr_stu_ix} ON {usr}(LOWER(studentid))');

677
678
        // Only one profile view per user
        execute_sql("CREATE UNIQUE INDEX {view_own_type_uix} ON {view}(owner) WHERE type = 'profile'");
679
680
    }

681
682
683
684
685
686
687
688
689
690
691
    // Some more advanced constraints. XMLDB can't handle this in its xml file format
    execute_sql('ALTER TABLE {artefact} ADD CHECK (
        (owner IS NOT NULL AND "group" IS NULL     AND institution IS NULL) OR
        (owner IS NULL     AND "group" IS NOT NULL AND institution IS NULL) OR
        (owner IS NULL     AND "group" IS NULL     AND institution IS NOT NULL)
    )');
    execute_sql('ALTER TABLE {view} ADD CHECK (
        (owner IS NOT NULL AND "group" IS NULL     AND institution IS NULL) OR
        (owner IS NULL     AND "group" IS NOT NULL AND institution IS NULL) OR
        (owner IS NULL     AND "group" IS NULL     AND institution IS NOT NULL)
    )');
692
693
694
695
    execute_sql('ALTER TABLE {artefact} ADD CHECK (
        (author IS NOT NULL AND authorname IS NULL    ) OR
        (author IS NULL     AND authorname IS NOT NULL)
    )');
696
    execute_sql('ALTER TABLE {view_access} ADD CHECK (
697
698
699
700
701
        (accesstype IS NOT NULL AND "group" IS NULL     AND usr IS NULL     AND token IS NULL   AND institution IS NULL) OR
        (accesstype IS NULL     AND "group" IS NOT NULL AND usr IS NULL     AND token IS NULL AND institution IS NULL) OR
        (accesstype IS NULL     AND "group" IS NULL     AND usr IS NOT NULL AND token IS NULL AND institution IS NULL) OR
        (accesstype IS NULL     AND "group" IS NULL     AND usr IS NULL     AND token IS NOT NULL AND institution IS NULL) OR
        (accesstype IS NULL     AND "group" IS NULL     AND usr IS NULL     AND token IS NULL AND institution IS NOT NULL)
702
    )');
703
704
705
706
707
    execute_sql('ALTER TABLE {collection} ADD CHECK (
        (owner IS NOT NULL AND "group" IS NULL     AND institution IS NULL) OR
        (owner IS NULL     AND "group" IS NOT NULL AND institution IS NULL) OR
        (owner IS NULL     AND "group" IS NULL     AND institution IS NOT NULL)
    )');
708

709
    set_antispam_defaults();
710
    reload_html_filters();
711
712

    // Default set of sites from which iframe content can be embedded
713
714
    // See also the postinst() function in plugins for other valid iframes
    // by searching for 'iframe_source_icon'
715
716
717
718
719
720
721
722
723
    $iframesources = array(
        'www.youtube.com/embed/'                   => 'YouTube',
        'player.vimeo.com/video/'                  => 'Vimeo',
        'www.slideshare.net/slideshow/embed_code/' => 'SlideShare',
        'www.glogster.com/glog/'                   => 'Glogster',
        'www.glogster.com/glog.php'                => 'Glogster',
        'edu.glogster.com/glog/'                   => 'Glogster',
        'edu.glogster.com/glog.php'                => 'Glogster',
        'wikieducator.org/index.php'               => 'WikiEducator',
724
        'voki.com/php/'                            => 'Voki',
725
726
727
728
729
730
731
    );
    $iframedomains = array(
        'YouTube'      => 'www.youtube.com',
        'Vimeo'        => 'vimeo.com',
        'SlideShare'   => 'www.slideshare.net',
        'Glogster'     => 'www.glogster.com',
        'WikiEducator' => 'wikieducator.org',
732
        'Voki'         => 'voki.com',
733
734
735
    );
    update_safe_iframes($iframesources, $iframedomains);

736
737
738
    require_once(get_config('docroot') . 'lib/file.php');
    update_magicdb_path();

739
    return $status;
740
741
}

742
function core_install_lastcoredata_defaults() {
743
    global $USER;
744
    db_begin();
745
    $institution = new stdClass();
746
747
748
    $institution->name = 'mahara';
    $institution->displayname = 'No Institution';
    $institution->authplugin  = 'internal';
749
    $institution->theme  = 'default';
750
    $institution->priority = 0;
751
    insert_record('institution', $institution);
752

753
    $auth_instance = new stdClass();
754
    $auth_instance->instancename  = 'Internal';
755
    $auth_instance->priority='1';
756
757
    $auth_instance->institution   = 'mahara';
    $auth_instance->authname      = 'internal';
758
    $auth_instance->active        = 1;
759
760
    $auth_instance->id = insert_record('auth_instance', $auth_instance, 'id', true);

761
    // Insert the root user
762
    $userid = 0;
763
    $user = new stdClass();
764
    $user->id = $userid;
765
766
767
768
769
770
    $user->username = 'root';
    $user->password = '*';
    $user->salt = '*';
    $user->firstname = 'System';
    $user->lastname = 'User';
    $user->email = 'root@example.org';
771
    $user->quota = get_config_plugin('artefact', 'file', 'defaultquota');
772
    $user->authinstance = $auth_instance->id;
773
    $user->admin = 1;
774

775
776
    if (is_mysql()) { // gratuitous mysql workaround
        $newid = insert_record('usr', $user, 'id', true);
777
        set_field('usr', 'id', 0, 'id', $newid);
778
        execute_sql('ALTER TABLE {usr} AUTO_INCREMENT=1');
779
    }
780
781
782
    else {
        insert_record('usr', $user);
    }
783

784
785
786
787
788
    $pages = site_content_pages();
    $now = db_format_timestamp(time());
    foreach ($pages as $name) {
        $page = new stdClass();
        $page->ctime = $now;
789
        $page->institution = 'mahara';
790
        $page->content = get_string($name . 'defaultcontent', 'install', get_string('staticpageconfigdefaults', 'install',  get_config('wwwroot') . 'admin/site/pages.php'));
791
        $page->name = $name;
792
793
794
795
796
        $page->mtime = $now;
        $page->mauthor = $userid;
        insert_record('site_content', $page);
    }

797
798
799
800
801
    $versionedpages = site_content_version_pages();
    foreach ($versionedpages as $name) {
        $page = new stdClass();
        $page->ctime = $now;
        $page->institution = 'mahara';
802
        $page->content = get_string($name . 'defaultcontent', 'install', get_string('versionedpageconfigdefault', 'install', get_config('wwwroot') . 'admin/site/privacy.php?fs=' . $name));
803
804
805
806
807
808
        $page->type = $name;
        $page->author = $userid;
        $page->version = '1.0';
        insert_record('site_content_version', $page);
    }

809
810
811
    // install the default layout options
    install_view_layout_defaults();

812
    require_once('group.php');
813
    install_system_profile_view();
814
    install_system_dashboard_view();
815
    install_system_grouphomepage_view();
816
817
    require_once('view.php');
    install_system_portfolio_view();
818

819
820
821
    require_once('license.php');
    install_licenses_default();

822
823
824
    require_once('skin.php');
    install_skins_default();

825
826
    install_auth_default();

827
828
829
830
    // Remove admin privs from root user as it doesn't need it now
    $user->admin = 0;
    update_record('usr', $user, array('id' => 0));

831
    // Insert the admin user
832
    $user = new stdClass();
833
    $user->username = 'admin';
834
    $user->salt = auth_get_random_salt();
835
    $user->password = crypt('mahara', '$2a$' . get_config('bcrypt_cost') . '$' . substr(md5(get_config('passwordsaltmain') . $user->salt), 0, 22));
836
    $user->password = substr($user->password, 0, 7) . substr($user->password, 7+22);
837
    $user->authinstance = $auth_instance->id;
838
839
840
841
842
    $user->passwordchange = 1;
    $user->admin = 1;
    $user->firstname = 'Admin';
    $user->lastname = 'User';
    $user->email = 'admin@example.org';
843
    $user->quota = get_config_plugin('artefact', 'file', 'defaultquota');
844
    $user->ctime = db_format_timestamp(time());
845
846
847
848
    $user->id = insert_record('usr', $user, 'id', true);
    set_profile_field($user->id, 'email', $user->email);
    set_profile_field($user->id, 'firstname', $user->firstname);
    set_profile_field($user->id, 'lastname', $user->lastname);
849
850
    // Accept the user privacy agreement on install
    $sitecontentid = get_field('site_content_version', 'id', 'type', 'privacy', 'institution', 'mahara');
851
852
853
    save_user_reply_to_agreement($user->id, $sitecontentid, 1);
    // Accept the user T&C on install
    $sitecontentid = get_field('site_content_version', 'id', 'type', 'termsandconditions', 'institution', 'mahara');
854
    save_user_reply_to_agreement($user->id, $sitecontentid, 1);
855
    handle_event('createuser', $user, array('password'));
856
    activity_add_admin_defaults(array($user->id));
857
    db_commit();
858
859

    // if we're installing, set up the block categories here and then poll the plugins.
Elliot Pahl's avatar
Elliot Pahl committed
860
    // if we're upgrading this happens somewhere else.  This is because of dependency issues around
861
    // the order of installation stuff.
862
    install_blocktype_extras();
863
864
865
866
867
868

    // Setting user roles for content block access
    $table = new XMLDBTable('usr_roles');

    $roles = array('peer' => 0, 'manager' => 1, 'peermanager' => 1);
    foreach ($roles as $role => $state) {
869
        $obj = new stdClass();
870
871
872
873
        $obj->role              = $role;
        $obj->see_block_content = $state;
        insert_record('usr_roles', $obj);
    }
874
875
}

876
877
878
function core_install_firstcoredata_defaults() {
    // Install the default institution
    db_begin();
Elliot Pahl's avatar
Elliot Pahl committed
879

880
    set_config('session_timeout', 86400);
881
    set_config('sitename', 'Mahara');
882
    set_config('defaultregistrationexpirylifetime', 1209600);
883
    set_config('defaultaccountinactivewarn', 604800);
884
    set_config('creategroups', 'all');
885
    set_config('createpublicgroups', 'all');
886
    set_config('allowpublicviews', 1);
887
    set_config('allowpublicprofiles', 1);
888
    set_config('allowanonymouspages', 0);
889
    set_config('generatesitemap', 1);
890
891
892
    set_config('showselfsearchsideblock', 0);
    set_config('showtagssideblock', 1);
    set_config('tagssideblockmaxtags', 20);
893
    set_config('usersallowedmultipleinstitutions', 1);
894
    set_config('userscanchooseviewthemes', 0);
895
    set_config('anonymouscomments', 1);
896
    set_config('homepageinfo', 1);
897
    set_config('showonlineuserssideblock', 1);
898
    set_config('footerlinks', serialize(array('legal', 'about', 'contactus', 'manualhelp')));
899
    set_config('nousernames', 0);
900
    set_config('onlineuserssideblockmaxusers', 10);
901
    set_config('loggedinprofileviewaccess', 1);
902
    set_config('dropdownmenu', 0);
903
904
    // Set this to a random starting number to make minor version slightly harder to detect
    set_config('cacheversion', rand(1000, 9999));
905
    set_config('watchlistnotification_delay', 20);
906
907

    // install the applications
908
    $app = new stdClass();
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
    $app->name = 'mahara';
    $app->displayname = 'Mahara';
    $app->xmlrpcserverurl = '/api/xmlrpc/server.php';
    $app->ssolandurl = '/auth/xmlrpc/land.php';
    insert_record('application', $app);

    $app->name = 'moodle';
    $app->displayname = 'Moodle';
    $app->xmlrpcserverurl = '/mnet/xmlrpc/server.php';
    $app->ssolandurl = '/auth/mnet/land.php';
    insert_record('application', $app);

    // insert the event types
    $eventtypes = array(
        'createuser',
        'updateuser',
        'suspenduser',
        'unsuspenduser',
        'deleteuser',
        'undeleteuser',
        'expireuser',
        'unexpireuser',
        'deactivateuser',
        'activateuser',
933
        'userjoinsgroup',
934
        'userleavesgroup',
935
        'userchangegrouprole',
936
937
        'saveartefact',
        'deleteartefact',
938
        'deleteartefacts',
939
940
        'saveview',
        'deleteview',
941
        'blockinstancecommit',
942
        'deleteblockinstance',
943
944
945
946
        'addfriend',
        'removefriend',
        'addfriendrequest',
        'removefriendrequest',
Richard Mansfield's avatar
Richard Mansfield committed
947
        'creategroup',
948
        'loginas',
949
        'clearcaches',
950
951
952
953
954
955
        'createview',
        'createcollection',
        'updatecollection',
        'deletecollection',
        'addsubmission',
        'releasesubmission',
956
957
        'updateviewaccess',
        'sharedcommenttogroup'
958
959
960
    );

    foreach ($eventtypes as $et) {
961
        $e = new stdClass();
962
963
964
965
        $e->name = $et;
        insert_record('event_type', $e);
    }

966
967
968
969
970
    // install the core event subscriptions
    $subs = array(
        array(
            'event'        => 'createuser',
            'callfunction' => 'activity_set_defaults',
971
        ),
972
973
974
975
        array(
            'event'        => 'createuser',
            'callfunction' => 'add_user_to_autoadd_groups',
        ),
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
        array(
            'event'         => 'blockinstancecommit',
            'callfunction'  => 'watchlist_record_changes',
        ),
        array(
            'event'         => 'deleteblockinstance',
            'callfunction'  => 'watchlist_block_deleted',
        ),
        array(
            'event'         => 'saveartefact',
            'callfunction'  => 'watchlist_record_changes',
        ),
        array(
            'event'         => 'saveview',
            'callfunction'  => 'watchlist_record_changes',
        ),
992
993
994
995
996
    );

    foreach ($subs as $sub) {
        insert_record('event_subscription', (object)$sub);
    }
Elliot Pahl's avatar
Elliot Pahl committed
997

998
    // Install the activity types. Name, admin, delay, allownonemethod, defaultmethod.
999
    $activitytypes = array(
1000