Many bugs and typos fixed in capabilitties and legacy isteacher() and similar functi...
[moodle.git] / lib / adminlib.php
CommitLineData
88a7228a 1<?php //
2 //
3
4/**
5 * adminlib.php - Contains functions that only administrators will ever need to use
6 *
7 * @author Martin Dougiamas
8 * @version $Id$
9 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
10 * @package moodlecore
11 */
12
13/**
ead29342 14 * Upgrade plugins
88a7228a 15 *
16 * @uses $db
17 * @uses $CFG
ead29342 18 * @param string $type The type of plugins that should be updated (e.g. 'enrol', 'qtype')
19 * @param string $dir The directory where the plugins are located (e.g. 'question/questiontypes')
20 * @param string $return The url to prompt the user to continue to
88a7228a 21 */
ead29342 22function upgrade_plugins($type, $dir, $return) {
e69ef14b 23 global $CFG, $db;
173cc1c3 24
ead29342 25 if (!$plugs = get_list_of_plugins($dir) ) {
26 error('No '.$type.' plugins installed!');
173cc1c3 27 }
28
583fad99 29 $updated_plugins = false;
30 $strpluginsetup = get_string('pluginsetup');
31
ead29342 32 foreach ($plugs as $plug) {
173cc1c3 33
ead29342 34 $fullplug = $CFG->dirroot .'/'.$dir.'/'. $plug;
173cc1c3 35
ead29342 36 unset($plugin);
173cc1c3 37
bbbf2d40 38 if (is_readable($fullplug .'/version.php')) {
ead29342 39 include_once($fullplug .'/version.php'); // defines $plugin with version etc
173cc1c3 40 } else {
41 continue; // Nothing to do.
42 }
43
bbbf2d40 44 if (is_readable($fullplug .'/db/'. $CFG->dbtype .'.php')) {
ead29342 45 include_once($fullplug .'/db/'. $CFG->dbtype .'.php'); // defines upgrading function
173cc1c3 46 } else {
47 continue;
48 }
49
ead29342 50 if (!isset($plugin)) {
173cc1c3 51 continue;
52 }
53
ead29342 54 if (!empty($plugin->requires)) {
55 if ($plugin->requires > $CFG->version) {
56 $info->pluginname = $plug;
57 $info->pluginversion = $plugin->version;
173cc1c3 58 $info->currentmoodle = $CFG->version;
ead29342 59 $info->requiremoodle = $plugin->requires;
583fad99 60 if (!$updated_plugins) {
61 print_header($strpluginsetup, $strpluginsetup, $strpluginsetup, '',
62 '<script type="text/javascript" src="' . $CFG->wwwroot . '/lib/scroll_to_errors.js"></script>',
63 false, '&nbsp;', '&nbsp;');
64 }
65 upgrade_log_start();
ead29342 66 notify(get_string('pluginrequirementsnotmet', 'error', $info));
583fad99 67 $updated_plugins = true;
173cc1c3 68 unset($info);
69 continue;
70 }
71 }
72
ead29342 73 $plugin->name = $plug; // The name MUST match the directory
173cc1c3 74
ead29342 75 $pluginversion = $type.'_'.$plug.'_version';
173cc1c3 76
ead29342 77 if (!isset($CFG->$pluginversion)) {
78 set_config($pluginversion, 0);
173cc1c3 79 }
80
ead29342 81 if ($CFG->$pluginversion == $plugin->version) {
173cc1c3 82 // do nothing
ead29342 83 } else if ($CFG->$pluginversion < $plugin->version) {
583fad99 84 if (!$updated_plugins) {
a36f058e 85 print_header($strpluginsetup, $strpluginsetup, $strpluginsetup, '',
86 '<script type="text/javascript" src="' . $CFG->wwwroot . '/lib/scroll_to_errors.js"></script>',
87 false, '&nbsp;', '&nbsp;');
173cc1c3 88 }
583fad99 89 upgrade_log_start();
ead29342 90 print_heading($plugin->name .' plugin needs upgrading');
d87a9d73 91 if ($CFG->$pluginversion == 0) { // It's a new install of this plugin
92 if (file_exists($fullplug .'/db/'. $CFG->dbtype .'.sql')) {
93 $db->debug = true;
94 @set_time_limit(0); // To allow slow databases to complete the long SQL
95 if (modify_database($fullplug .'/db/'. $CFG->dbtype .'.sql')) {
96 // OK so far, now update the plugins record
97 set_config($pluginversion, $plugin->version);
e6316185 98 if (!update_capabilities($dir.'/'.$plug)) {
bbbf2d40 99 error('Could not set up the capabilities for '.$module->name.'!');
100 }
d87a9d73 101 notify(get_string('modulesuccess', '', $plugin->name), 'notifysuccess');
102 } else {
103 notify('Installing '. $plugin->name .' FAILED!');
104 }
105 $db->debug = false;
106 } else { // We'll assume no tables are necessary
ead29342 107 set_config($pluginversion, $plugin->version);
108 notify(get_string('modulesuccess', '', $plugin->name), 'notifysuccess');
d87a9d73 109 }
110 } else { // Upgrade existing install
111 $upgrade_function = $type.'_'.$plugin->name .'_upgrade';
112 if (function_exists($upgrade_function)) {
113 $db->debug=true;
114 if ($upgrade_function($CFG->$pluginversion)) {
115 $db->debug=false;
116 // OK so far, now update the plugins record
117 set_config($pluginversion, $plugin->version);
e6316185 118 if (!update_capabilities($dir.'/'.$plug)) {
bbbf2d40 119 error('Could not update '.$plugin->name.' capabilities!');
120 }
d87a9d73 121 notify(get_string('modulesuccess', '', $plugin->name), 'notifysuccess');
122 } else {
123 $db->debug=false;
124 notify('Upgrading '. $plugin->name .' from '. $CFG->$pluginversion .' to '. $plugin->version .' FAILED!');
125 }
173cc1c3 126 }
127 }
d87a9d73 128 echo '<hr />';
ead29342 129 $updated_plugins = true;
173cc1c3 130 } else {
583fad99 131 upgrade_log_start();
ead29342 132 error('Version mismatch: '. $plugin->name .' can\'t downgrade '. $CFG->$pluginversion .' -> '. $plugin->version .' !');
173cc1c3 133 }
134 }
135
583fad99 136 upgrade_log_finish();
137
138 if ($updated_plugins) {
173cc1c3 139 print_continue($return);
140 die;
141 }
142}
143
88a7228a 144/**
145 * Find and check all modules and load them up or upgrade them if necessary
146 *
147 * @uses $db
148 * @uses $CFG
149 * @param string $return The url to prompt the user to continue to
150 * @todo Finish documenting this function
151 */
173cc1c3 152function upgrade_activity_modules($return) {
173cc1c3 153
e69ef14b 154 global $CFG, $db;
173cc1c3 155
88a7228a 156 if (!$mods = get_list_of_plugins('mod') ) {
157 error('No modules installed!');
173cc1c3 158 }
159
583fad99 160 $updated_modules = false;
161 $strmodulesetup = get_string('modulesetup');
162
173cc1c3 163 foreach ($mods as $mod) {
164
88a7228a 165 if ($mod == 'NEWMODULE') { // Someone has unzipped the template, ignore it
173cc1c3 166 continue;
167 }
168
88a7228a 169 $fullmod = $CFG->dirroot .'/mod/'. $mod;
173cc1c3 170
171 unset($module);
172
88a7228a 173 if ( is_readable($fullmod .'/version.php')) {
174 include_once($fullmod .'/version.php'); // defines $module with version etc
173cc1c3 175 } else {
88a7228a 176 notify('Module '. $mod .': '. $fullmod .'/version.php was not readable');
173cc1c3 177 continue;
178 }
179
88a7228a 180 if ( is_readable($fullmod .'/db/'. $CFG->dbtype .'.php')) {
181 include_once($fullmod .'/db/'. $CFG->dbtype .'.php'); // defines upgrading function
173cc1c3 182 } else {
88a7228a 183 notify('Module '. $mod .': '. $fullmod .'/db/'. $CFG->dbtype .'.php was not readable');
173cc1c3 184 continue;
185 }
186
187 if (!isset($module)) {
188 continue;
189 }
190
191 if (!empty($module->requires)) {
192 if ($module->requires > $CFG->version) {
193 $info->modulename = $mod;
194 $info->moduleversion = $module->version;
195 $info->currentmoodle = $CFG->version;
196 $info->requiremoodle = $module->requires;
583fad99 197 if (!$updated_modules) {
198 print_header($strmodulesetup, $strmodulesetup, $strmodulesetup, '',
199 '<script type="text/javascript" src="' . $CFG->wwwroot . '/lib/scroll_to_errors.js"></script>',
200 false, '&nbsp;', '&nbsp;');
201 }
202 upgrade_log_start();
173cc1c3 203 notify(get_string('modulerequirementsnotmet', 'error', $info));
583fad99 204 $updated_modules = true;
173cc1c3 205 unset($info);
206 continue;
207 }
208 }
209
210 $module->name = $mod; // The name MUST match the directory
211
88a7228a 212 if ($currmodule = get_record('modules', 'name', $module->name)) {
173cc1c3 213 if ($currmodule->version == $module->version) {
214 // do nothing
215 } else if ($currmodule->version < $module->version) {
583fad99 216 if (!$updated_modules) {
a36f058e 217 print_header($strmodulesetup, $strmodulesetup, $strmodulesetup, '',
218 '<script type="text/javascript" src="' . $CFG->wwwroot . '/lib/scroll_to_errors.js"></script>',
219 false, '&nbsp;', '&nbsp;');
173cc1c3 220 }
583fad99 221 upgrade_log_start();
88a7228a 222 print_heading($module->name .' module needs upgrading');
bbbf2d40 223
224 // Run the upgrade function for the module.
88a7228a 225 $upgrade_function = $module->name.'_upgrade';
173cc1c3 226 if (function_exists($upgrade_function)) {
227 $db->debug=true;
228 if ($upgrade_function($currmodule->version, $module)) {
229 $db->debug=false;
230 // OK so far, now update the modules record
231 $module->id = $currmodule->id;
88a7228a 232 if (! update_record('modules', $module)) {
233 error('Could not update '. $module->name .' record in modules table!');
173cc1c3 234 }
d6ead3a2 235 remove_dir($CFG->dataroot . '/cache', true); // flush cache
a8f68426 236 notify(get_string('modulesuccess', '', $module->name), 'notifysuccess');
88a7228a 237 echo '<hr />';
173cc1c3 238 } else {
239 $db->debug=false;
88a7228a 240 notify('Upgrading '. $module->name .' from '. $currmodule->version .' to '. $module->version .' FAILED!');
173cc1c3 241 }
242 }
bbbf2d40 243
244 // Update the capabilities table?
245 if (!update_capabilities('mod/'.$module->name)) {
246 error('Could not update '.$module->name.' capabilities!');
247 }
248
173cc1c3 249 $updated_modules = true;
bbbf2d40 250
173cc1c3 251 } else {
583fad99 252 upgrade_log_start();
88a7228a 253 error('Version mismatch: '. $module->name .' can\'t downgrade '. $currmodule->version .' -> '. $module->version .' !');
173cc1c3 254 }
255
256 } else { // module not installed yet, so install it
583fad99 257 if (!$updated_modules) {
a36f058e 258 print_header($strmodulesetup, $strmodulesetup, $strmodulesetup, '',
259 '<script type="text/javascript" src="' . $CFG->wwwroot . '/lib/scroll_to_errors.js"></script>',
260 false, '&nbsp;', '&nbsp;');
173cc1c3 261 }
583fad99 262 upgrade_log_start();
173cc1c3 263 print_heading($module->name);
264 $updated_modules = true;
265 $db->debug = true;
266 @set_time_limit(0); // To allow slow databases to complete the long SQL
88a7228a 267 if (modify_database($fullmod .'/db/'. $CFG->dbtype .'.sql')) {
173cc1c3 268 $db->debug = false;
88a7228a 269 if ($module->id = insert_record('modules', $module)) {
bbbf2d40 270 if (!update_capabilities('mod/'.$module->name)) {
271 error('Could not set up the capabilities for '.$module->name.'!');
272 }
a8f68426 273 notify(get_string('modulesuccess', '', $module->name), 'notifysuccess');
88a7228a 274 echo '<hr />';
173cc1c3 275 } else {
88a7228a 276 error($module->name .' module could not be added to the module list!');
173cc1c3 277 }
278 } else {
88a7228a 279 error($module->name .' tables could NOT be set up successfully!');
173cc1c3 280 }
281 }
e5bd4e58 282
283 /// Check submodules of this module if necessary
284
285 include_once($fullmod.'/lib.php'); // defines upgrading function
286
287 $submoduleupgrade = $module->name.'_upgrade_submodules';
288 if (function_exists($submoduleupgrade)) {
289 $submoduleupgrade();
290 }
291
292
293 /// Run any defaults or final code that is necessary for this module
294
a5c0990e 295 if ( is_readable($fullmod .'/defaults.php')) {
296 // Insert default values for any important configuration variables
9e6e7502 297 unset($defaults);
a5c0990e 298 include_once($fullmod .'/defaults.php');
f9a2e515 299 if (!empty($defaults)) {
300 foreach ($defaults as $name => $value) {
301 if (!isset($CFG->$name)) {
302 set_config($name, $value);
303 }
a5c0990e 304 }
305 }
306 }
173cc1c3 307 }
308
583fad99 309 upgrade_log_finish(); // finish logging if started
310
311 if ($updated_modules) {
173cc1c3 312 print_continue($return);
136f43dc 313 print_footer();
173cc1c3 314 die;
315 }
316}
317
f3221af9 318/**
319 * This function will return FALSE if the lock fails to be set (ie, if it's already locked)
80be7ee3 320 *
321 * @param string $name ?
322 * @param bool $value ?
323 * @param int $staleafter ?
324 * @param bool $clobberstale ?
325 * @todo Finish documenting this function
f3221af9 326 */
327function set_cron_lock($name,$value=true,$staleafter=7200,$clobberstale=false) {
328
329 if (empty($name)) {
330 mtrace("Tried to get a cron lock for a null fieldname");
331 return false;
332 }
333
334 if (empty($value)) {
335 set_config($name,0);
336 return true;
337 }
338
339 if ($config = get_record('config','name',$name)) {
340 if (empty($config->value)) {
341 set_config($name,time());
342 } else {
343 // check for stale.
344 if ((time() - $staleafter) > $config->value) {
345 mtrace("STALE LOCKFILE FOR $name - was $config->value");
346 if (!empty($clobberstale)) {
347 set_config($name,time());
348 return true;
349 }
350 } else {
351 return false; // was not stale - ie, we're ok to still be running.
352 }
353 }
354 }
355 else {
356 set_config($name,time());
357 }
358 return true;
359}
a597f8a8 360
fb06b255 361function print_progress($done, $total, $updatetime=5, $sleeptime=1, $donetext='') {
a597f8a8 362 static $starttime;
363 static $lasttime;
364
365 if (empty($starttime)) {
366 $starttime = $lasttime = time();
367 $lasttime = $starttime - $updatetime;
368 echo '<table width="500" cellpadding="0" cellspacing="0" align="center"><tr><td width="500">';
369 echo '<div id="bar" style="border-style:solid;border-width:1px;width:500px;height:50px;">';
370 echo '<div id="slider" style="border-style:solid;border-width:1px;height:48px;width:10px;background-color:green;"></div>';
371 echo '</div>';
372 echo '<div id="text" align="center" style="width:500px;"></div>';
373 echo '</td></tr></table>';
374 echo '</div>';
375 }
376
a597f8a8 377 $now = time();
378
379 if ($done && (($now - $lasttime) >= $updatetime)) {
380 $elapsedtime = $now - $starttime;
381 $projectedtime = (int)(((float)$total / (float)$done) * $elapsedtime) - $elapsedtime;
382 $percentage = format_float((float)$done / (float)$total, 2);
383 $width = (int)(500 * $percentage);
384
fb06b255 385 if ($projectedtime > 10) {
386 $projectedtext = ' Ending: '.format_time($projectedtime);
387 } else {
388 $projectedtext = '';
389 }
390
a597f8a8 391 echo '<script>';
fb06b255 392 echo 'document.getElementById("text").innerHTML = "'.addslashes($donetext).' '.$done.' done.'.$projectedtext.'";'."\n";
a597f8a8 393 echo 'document.getElementById("slider").style.width = \''.$width.'px\';'."\n";
394 echo '</script>';
395
396 $lasttime = $now;
397 sleep($sleeptime);
398 }
399}
583fad99 400
401////////////////////////////////////////////////
402/// upgrade logging functions
403////////////////////////////////////////////////
404
405$upgradeloghandle = false;
26c91c73 406$upgradelogbuffer = '';
407// I did not find out how to use static variable in callback function,
408// the problem was that I could not flush the static buffer :-(
409global $upgradeloghandle, $upgradelogbuffer;
583fad99 410
411/**
412 * Check if upgrade is already running.
413 *
414 * If anything goes wrong due to missing call to upgrade_log_finish()
415 * just restart the browser.
416 *
417 * @param string warning message indicating upgrade is already running
418 * @param int page reload timeout
419 */
420function upgrade_check_running($message, $timeout) {
421 if (!empty($_SESSION['upgraderunning'])) {
422 print_header();
423 redirect(me(), $message, $timeout);
424 }
425}
426
427/**
428 * Start logging of output into file (if not disabled) and
429 * prevent aborting and concurrent execution of upgrade script.
430 *
431 * Please note that you can not write into session variables after calling this function!
432 *
433 * This function may be called repeatedly.
434 */
435function upgrade_log_start() {
436 global $upgradeloghandle;
437
438 if (!empty($_SESSION['upgraderunning'])) {
439 return; // logging already started
440 }
441
442 @ignore_user_abort(true); // ignore if user stops or otherwise aborts page loading
443 $_SESSION['upgraderunning'] = 1; // set upgrade indicator
444 session_write_close(); // from now on user can reload page - will be displayed warning
445 make_upload_directory('upgradelogs');
446 ob_start('upgrade_log_callback', 2); // function for logging to disk; flush each line of text ASAP
dedb2304 447 register_shutdown_function('upgrade_log_finish'); // in case somebody forgets to stop logging
583fad99 448}
449
450/**
451 * Terminate logging of output, flush all data, allow script aborting
452 * and reopen session for writing. Function error() does terminate the logging too.
453 *
454 * Please make sure that each upgrade_log_start() is properly terminated by
455 * this function or error().
456 *
457 * This function may be called repeatedly.
458 */
459function upgrade_log_finish() {
26c91c73 460 global $upgradeloghandle, $upgradelogbuffer;
583fad99 461
462 if (empty($_SESSION['upgraderunning'])) {
463 return; // logging already terminated
464 }
465
466 @ob_end_flush();
26c91c73 467 if ($upgradelogbuffer !== '') {
468 @fwrite($upgradeloghandle, $upgradelogbuffer);
40896537 469 $upgradelogbuffer = '';
26c91c73 470 }
471 if ($upgradeloghandle and ($upgradeloghandle !== 'error')) {
472 @fclose($upgradeloghandle);
40896537 473 $upgradeloghandle = false;
26c91c73 474 }
583fad99 475 @session_start(); // ignore header errors, we only need to reopen session
476 $_SESSION['upgraderunning'] = 0; // clear upgrade indicator
477 if (connection_aborted()) {
478 die;
479 }
480 @ignore_user_abort(false);
481}
482
483/**
484 * Callback function for logging into files. Not more than one file is created per minute,
485 * upgrade session (terminated by upgrade_log_finish()) is always stored in one file.
486 *
487 * This function must not output any characters or throw warnigns and errors!
488 */
489function upgrade_log_callback($string) {
26c91c73 490 global $CFG, $upgradeloghandle, $upgradelogbuffer;
583fad99 491
492 if (empty($CFG->disableupgradelogging) and ($string != '') and ($upgradeloghandle !== 'error')) {
493 if ($upgradeloghandle or ($upgradeloghandle = @fopen($CFG->dataroot.'/upgradelogs/upg_'.date('Ymd-Hi').'.html', 'a'))) {
26c91c73 494 $upgradelogbuffer .= $string;
495 if (strlen($upgradelogbuffer) > 2048) { // 2kB write buffer
496 @fwrite($upgradeloghandle, $upgradelogbuffer);
497 $upgradelogbuffer = '';
498 }
583fad99 499 } else {
500 $upgradeloghandle = 'error';
501 }
502 }
503 return $string;
504}
505
9e6e7502 506?>