PageRenderTime 57ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/adminlib.php

https://bitbucket.org/ceu/moodle_demo
PHP | 5050 lines | 3499 code | 659 blank | 892 comment | 618 complexity | 0f1381dcda92bc8559e7775be9e04e68 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.0, LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * adminlib.php - Contains functions that only administrators will ever need to use
  4. *
  5. * @author Martin Dougiamas and many others
  6. * @version $Id: adminlib.php,v 1.153.2.77 2011/08/26 16:22:55 moodlerobot Exp $
  7. * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
  8. * @package moodlecore
  9. */
  10. define('INSECURE_DATAROOT_WARNING', 1);
  11. define('INSECURE_DATAROOT_ERROR', 2);
  12. function upgrade_main_savepoint($result, $version) {
  13. global $CFG;
  14. if ($result) {
  15. if ($CFG->version >= $version) {
  16. // something really wrong is going on in main upgrade script
  17. error("Upgrade savepoint: Can not upgrade main version from $CFG->version to $version.");
  18. }
  19. set_config('version', $version);
  20. } else {
  21. notify ("Upgrade savepoint: Error during main upgrade to version $version");
  22. }
  23. }
  24. function upgrade_mod_savepoint($result, $version, $type) {
  25. //TODO
  26. }
  27. function upgrade_plugin_savepoint($result, $version, $type, $dir) {
  28. //TODO
  29. }
  30. function upgrade_backup_savepoint($result, $version) {
  31. //TODO
  32. }
  33. function upgrade_blocks_savepoint($result, $version, $type) {
  34. //TODO
  35. }
  36. /**
  37. * Upgrade plugins
  38. *
  39. * @uses $db
  40. * @uses $CFG
  41. * @param string $type The type of plugins that should be updated (e.g. 'enrol', 'qtype')
  42. * @param string $dir The directory where the plugins are located (e.g. 'question/questiontypes')
  43. * @param string $return The url to prompt the user to continue to
  44. */
  45. function upgrade_plugins($type, $dir, $return) {
  46. global $CFG, $db;
  47. /// Let's know if the header has been printed, so the funcion is being called embedded in an outer page
  48. $embedded = defined('HEADER_PRINTED');
  49. $plugs = get_list_of_plugins($dir);
  50. $updated_plugins = false;
  51. $strpluginsetup = get_string('pluginsetup');
  52. foreach ($plugs as $plug) {
  53. $fullplug = $CFG->dirroot .'/'.$dir.'/'. $plug;
  54. unset($plugin);
  55. if (is_readable($fullplug .'/version.php')) {
  56. include_once($fullplug .'/version.php'); // defines $plugin with version etc
  57. } else {
  58. continue; // Nothing to do.
  59. }
  60. $oldupgrade = false;
  61. $newupgrade = false;
  62. if (is_readable($fullplug . '/db/'. $CFG->dbtype . '.php')) {
  63. include_once($fullplug . '/db/'. $CFG->dbtype . '.php'); // defines old upgrading function
  64. $oldupgrade = true;
  65. }
  66. if (is_readable($fullplug . '/db/upgrade.php')) {
  67. include_once($fullplug . '/db/upgrade.php'); // defines new upgrading function
  68. $newupgrade = true;
  69. }
  70. if (!isset($plugin)) {
  71. continue;
  72. }
  73. if (!empty($plugin->requires)) {
  74. if ($plugin->requires > $CFG->version) {
  75. $info = new object();
  76. $info->pluginname = $plug;
  77. $info->pluginversion = $plugin->version;
  78. $info->currentmoodle = $CFG->version;
  79. $info->requiremoodle = $plugin->requires;
  80. if (!$updated_plugins && !$embedded) {
  81. print_header($strpluginsetup, $strpluginsetup,
  82. build_navigation(array(array('name' => $strpluginsetup, 'link' => null, 'type' => 'misc'))), '',
  83. upgrade_get_javascript(), false, '&nbsp;', '&nbsp;');
  84. }
  85. upgrade_log_start();
  86. notify(get_string('pluginrequirementsnotmet', 'error', $info));
  87. $updated_plugins = true;
  88. continue;
  89. }
  90. }
  91. $plugin->name = $plug; // The name MUST match the directory
  92. $pluginversion = $type.'_'.$plug.'_version';
  93. if (!isset($CFG->$pluginversion)) {
  94. set_config($pluginversion, 0);
  95. }
  96. if ($CFG->$pluginversion == $plugin->version) {
  97. // do nothing
  98. } else if ($CFG->$pluginversion < $plugin->version) {
  99. if (!$updated_plugins && !$embedded) {
  100. print_header($strpluginsetup, $strpluginsetup,
  101. build_navigation(array(array('name' => $strpluginsetup, 'link' => null, 'type' => 'misc'))), '',
  102. upgrade_get_javascript(), false, '&nbsp;', '&nbsp;');
  103. }
  104. $updated_plugins = true;
  105. upgrade_log_start();
  106. print_heading($dir.'/'. $plugin->name .' plugin needs upgrading');
  107. $db->debug = true;
  108. @set_time_limit(0); // To allow slow databases to complete the long SQL
  109. if ($CFG->$pluginversion == 0) { // It's a new install of this plugin
  110. /// Both old .sql files and new install.xml are supported
  111. /// but we priorize install.xml (XMLDB) if present
  112. $status = false;
  113. if (file_exists($fullplug . '/db/install.xml')) {
  114. $status = install_from_xmldb_file($fullplug . '/db/install.xml'); //New method
  115. } else if (file_exists($fullplug .'/db/'. $CFG->dbtype .'.sql')) {
  116. $status = modify_database($fullplug .'/db/'. $CFG->dbtype .'.sql'); //Old method
  117. } else {
  118. $status = true;
  119. }
  120. $db->debug = false;
  121. /// Continue with the instalation, roles and other stuff
  122. if ($status) {
  123. /// OK so far, now update the plugins record
  124. set_config($pluginversion, $plugin->version);
  125. /// Install capabilities
  126. if (!update_capabilities($type.'/'.$plug)) {
  127. error('Could not set up the capabilities for '.$plugin->name.'!');
  128. }
  129. /// Install events
  130. events_update_definition($type.'/'.$plug);
  131. /// Run local install function if there is one
  132. if (is_readable($fullplug .'/lib.php')) {
  133. include_once($fullplug .'/lib.php');
  134. $installfunction = $plugin->name.'_install';
  135. if (function_exists($installfunction)) {
  136. if (! $installfunction() ) {
  137. notify('Encountered a problem running install function for '.$plugin->name.'!');
  138. }
  139. }
  140. }
  141. notify(get_string('modulesuccess', '', $plugin->name), 'notifysuccess');
  142. } else {
  143. notify('Installing '. $plugin->name .' FAILED!');
  144. }
  145. } else { // Upgrade existing install
  146. /// Run de old and new upgrade functions for the module
  147. $oldupgrade_function = $type.'_'.$plugin->name .'_upgrade';
  148. $newupgrade_function = 'xmldb_' . $type.'_'.$plugin->name .'_upgrade';
  149. /// First, the old function if exists
  150. $oldupgrade_status = true;
  151. if ($oldupgrade && function_exists($oldupgrade_function)) {
  152. $db->debug = true;
  153. $oldupgrade_status = $oldupgrade_function($CFG->$pluginversion);
  154. } else if ($oldupgrade) {
  155. notify ('Upgrade function ' . $oldupgrade_function . ' was not available in ' .
  156. $fullplug . '/db/' . $CFG->dbtype . '.php');
  157. }
  158. /// Then, the new function if exists and the old one was ok
  159. $newupgrade_status = true;
  160. if ($newupgrade && function_exists($newupgrade_function) && $oldupgrade_status) {
  161. $db->debug = true;
  162. $newupgrade_status = $newupgrade_function($CFG->$pluginversion);
  163. } else if ($newupgrade) {
  164. notify ('Upgrade function ' . $newupgrade_function . ' was not available in ' .
  165. $fullplug . '/db/upgrade.php');
  166. }
  167. $db->debug=false;
  168. /// Now analyze upgrade results
  169. if ($oldupgrade_status && $newupgrade_status) { // No upgrading failed
  170. // OK so far, now update the plugins record
  171. set_config($pluginversion, $plugin->version);
  172. if (!update_capabilities($type.'/'.$plug)) {
  173. error('Could not update '.$plugin->name.' capabilities!');
  174. }
  175. events_update_definition($type.'/'.$plug);
  176. notify(get_string('modulesuccess', '', $plugin->name), 'notifysuccess');
  177. } else {
  178. notify('Upgrading '. $plugin->name .' from '. $CFG->$pluginversion .' to '. $plugin->version .' FAILED!');
  179. }
  180. }
  181. echo '<hr />';
  182. } else {
  183. upgrade_log_start();
  184. error('Version mismatch: '. $plugin->name .' can\'t downgrade '. $CFG->$pluginversion .' -> '. $plugin->version .' !');
  185. }
  186. }
  187. upgrade_log_finish();
  188. if ($updated_plugins && !$embedded) {
  189. print_continue($return);
  190. print_footer('none');
  191. die;
  192. }
  193. }
  194. /**
  195. * Find and check all modules and load them up or upgrade them if necessary
  196. *
  197. * @uses $db
  198. * @uses $CFG
  199. * @param string $return The url to prompt the user to continue to
  200. * @todo Finish documenting this function
  201. */
  202. function upgrade_activity_modules($return) {
  203. global $CFG, $db;
  204. if (!$mods = get_list_of_plugins('mod') ) {
  205. error('No modules installed!');
  206. }
  207. $updated_modules = false;
  208. $strmodulesetup = get_string('modulesetup');
  209. foreach ($mods as $mod) {
  210. if ($mod == 'NEWMODULE') { // Someone has unzipped the template, ignore it
  211. continue;
  212. }
  213. $fullmod = $CFG->dirroot .'/mod/'. $mod;
  214. unset($module);
  215. if ( is_readable($fullmod .'/version.php')) {
  216. include_once($fullmod .'/version.php'); // defines $module with version etc
  217. } else {
  218. notify('Module '. $mod .': '. $fullmod .'/version.php was not readable');
  219. continue;
  220. }
  221. $oldupgrade = false;
  222. $newupgrade = false;
  223. if ( is_readable($fullmod .'/db/' . $CFG->dbtype . '.php')) {
  224. include_once($fullmod .'/db/' . $CFG->dbtype . '.php'); // defines old upgrading function
  225. $oldupgrade = true;
  226. }
  227. if ( is_readable($fullmod . '/db/upgrade.php')) {
  228. include_once($fullmod . '/db/upgrade.php'); // defines new upgrading function
  229. $newupgrade = true;
  230. }
  231. if (!isset($module)) {
  232. continue;
  233. }
  234. if (!empty($module->requires)) {
  235. if ($module->requires > $CFG->version) {
  236. $info = new object();
  237. $info->modulename = $mod;
  238. $info->moduleversion = $module->version;
  239. $info->currentmoodle = $CFG->version;
  240. $info->requiremoodle = $module->requires;
  241. if (!$updated_modules) {
  242. print_header($strmodulesetup, $strmodulesetup,
  243. build_navigation(array(array('name' => $strmodulesetup, 'link' => null, 'type' => 'misc'))), '',
  244. upgrade_get_javascript(), false, '&nbsp;', '&nbsp;');
  245. }
  246. upgrade_log_start();
  247. notify(get_string('modulerequirementsnotmet', 'error', $info));
  248. $updated_modules = true;
  249. continue;
  250. }
  251. }
  252. $module->name = $mod; // The name MUST match the directory
  253. include_once($fullmod.'/lib.php'); // defines upgrading and/or installing functions
  254. if ($currmodule = get_record('modules', 'name', $module->name)) {
  255. if ($currmodule->version == $module->version) {
  256. // do nothing
  257. } else if ($currmodule->version < $module->version) {
  258. /// If versions say that we need to upgrade but no upgrade files are available, notify and continue
  259. if (!$oldupgrade && !$newupgrade) {
  260. notify('Upgrade files ' . $mod . ': ' . $fullmod . '/db/' . $CFG->dbtype . '.php or ' .
  261. $fullmod . '/db/upgrade.php were not readable');
  262. continue;
  263. }
  264. if (!$updated_modules) {
  265. print_header($strmodulesetup, $strmodulesetup,
  266. build_navigation(array(array('name' => $strmodulesetup, 'link' => null, 'type' => 'misc'))), '',
  267. upgrade_get_javascript(), false, '&nbsp;', '&nbsp;');
  268. }
  269. upgrade_log_start();
  270. print_heading($module->name .' module needs upgrading');
  271. /// Run de old and new upgrade functions for the module
  272. $oldupgrade_function = $module->name . '_upgrade';
  273. $newupgrade_function = 'xmldb_' . $module->name . '_upgrade';
  274. /// First, the old function if exists
  275. $oldupgrade_status = true;
  276. if ($oldupgrade && function_exists($oldupgrade_function)) {
  277. $db->debug = true;
  278. $oldupgrade_status = $oldupgrade_function($currmodule->version, $module);
  279. if (!$oldupgrade_status) {
  280. notify ('Upgrade function ' . $oldupgrade_function .
  281. ' did not complete successfully.');
  282. }
  283. } else if ($oldupgrade) {
  284. notify ('Upgrade function ' . $oldupgrade_function . ' was not available in ' .
  285. $mod . ': ' . $fullmod . '/db/' . $CFG->dbtype . '.php');
  286. }
  287. /// Then, the new function if exists and the old one was ok
  288. $newupgrade_status = true;
  289. if ($newupgrade && function_exists($newupgrade_function) && $oldupgrade_status) {
  290. $db->debug = true;
  291. $newupgrade_status = $newupgrade_function($currmodule->version, $module);
  292. } else if ($newupgrade && $oldupgrade_status) {
  293. notify ('Upgrade function ' . $newupgrade_function . ' was not available in ' .
  294. $mod . ': ' . $fullmod . '/db/upgrade.php');
  295. }
  296. $db->debug=false;
  297. /// Now analyze upgrade results
  298. if ($oldupgrade_status && $newupgrade_status) { // No upgrading failed
  299. // OK so far, now update the modules record
  300. $module->id = $currmodule->id;
  301. if (! update_record('modules', $module)) {
  302. error('Could not update '. $module->name .' record in modules table!');
  303. }
  304. remove_dir($CFG->dataroot . '/cache', true); // flush cache
  305. notify(get_string('modulesuccess', '', $module->name), 'notifysuccess');
  306. echo '<hr />';
  307. } else {
  308. notify('Upgrading '. $module->name .' from '. $currmodule->version .' to '. $module->version .' FAILED!');
  309. }
  310. /// Update the capabilities table?
  311. if (!update_capabilities('mod/'.$module->name)) {
  312. error('Could not update '.$module->name.' capabilities!');
  313. }
  314. events_update_definition('mod/'.$module->name);
  315. $updated_modules = true;
  316. } else {
  317. upgrade_log_start();
  318. error('Version mismatch: '. $module->name .' can\'t downgrade '. $currmodule->version .' -> '. $module->version .' !');
  319. }
  320. } else { // module not installed yet, so install it
  321. if (!$updated_modules) {
  322. print_header($strmodulesetup, $strmodulesetup,
  323. build_navigation(array(array('name' => $strmodulesetup, 'link' => null, 'type' => 'misc'))), '',
  324. upgrade_get_javascript(), false, '&nbsp;', '&nbsp;');
  325. }
  326. upgrade_log_start();
  327. print_heading($module->name);
  328. $updated_modules = true;
  329. $db->debug = true;
  330. @set_time_limit(0); // To allow slow databases to complete the long SQL
  331. /// Both old .sql files and new install.xml are supported
  332. /// but we priorize install.xml (XMLDB) if present
  333. if (file_exists($fullmod . '/db/install.xml')) {
  334. $status = install_from_xmldb_file($fullmod . '/db/install.xml'); //New method
  335. } else {
  336. $status = modify_database($fullmod .'/db/'. $CFG->dbtype .'.sql'); //Old method
  337. }
  338. $db->debug = false;
  339. /// Continue with the installation, roles and other stuff
  340. if ($status) {
  341. if ($module->id = insert_record('modules', $module)) {
  342. /// Capabilities
  343. if (!update_capabilities('mod/'.$module->name)) {
  344. error('Could not set up the capabilities for '.$module->name.'!');
  345. }
  346. /// Events
  347. events_update_definition('mod/'.$module->name);
  348. /// Run local install function if there is one
  349. $installfunction = $module->name.'_install';
  350. if (function_exists($installfunction)) {
  351. if (! $installfunction() ) {
  352. notify('Encountered a problem running install function for '.$module->name.'!');
  353. }
  354. }
  355. notify(get_string('modulesuccess', '', $module->name), 'notifysuccess');
  356. echo '<hr />';
  357. } else {
  358. error($module->name .' module could not be added to the module list!');
  359. }
  360. } else {
  361. error($module->name .' tables could NOT be set up successfully!');
  362. }
  363. }
  364. /// Check submodules of this module if necessary
  365. $submoduleupgrade = $module->name.'_upgrade_submodules';
  366. if (function_exists($submoduleupgrade)) {
  367. $submoduleupgrade();
  368. }
  369. /// Run any defaults or final code that is necessary for this module
  370. if ( is_readable($fullmod .'/defaults.php')) {
  371. // Insert default values for any important configuration variables
  372. unset($defaults);
  373. include($fullmod .'/defaults.php'); // include here means execute, not library include
  374. if (!empty($defaults)) {
  375. foreach ($defaults as $name => $value) {
  376. if (!isset($CFG->$name)) {
  377. set_config($name, $value);
  378. }
  379. }
  380. }
  381. }
  382. }
  383. upgrade_log_finish(); // finish logging if started
  384. if ($updated_modules) {
  385. print_continue($return);
  386. print_footer('none');
  387. die;
  388. }
  389. }
  390. /**
  391. * Try to obtain or release the cron lock.
  392. *
  393. * @param string $name name of lock
  394. * @param int $until timestamp when this lock considered stale, null means remove lock unconditionaly
  395. * @param bool $ignorecurrent ignore current lock state, usually entend previous lock
  396. * @return bool true if lock obtained
  397. */
  398. function set_cron_lock($name, $until, $ignorecurrent=false) {
  399. if (empty($name)) {
  400. debugging("Tried to get a cron lock for a null fieldname");
  401. return false;
  402. }
  403. // remove lock by force == remove from config table
  404. if (is_null($until)) {
  405. set_config($name, null);
  406. return true;
  407. }
  408. if (!$ignorecurrent) {
  409. // read value from db - other processes might have changed it
  410. $value = get_field('config', 'value', 'name', $name);
  411. if ($value and $value > time()) {
  412. //lock active
  413. return false;
  414. }
  415. }
  416. set_config($name, $until);
  417. return true;
  418. }
  419. function print_progress($done, $total, $updatetime=5, $sleeptime=1, $donetext='') {
  420. static $thisbarid;
  421. static $starttime;
  422. static $lasttime;
  423. if ($total < 2) { // No need to show anything
  424. return;
  425. }
  426. // Are we done?
  427. if ($done >= $total) {
  428. $done = $total;
  429. if (!empty($thisbarid)) {
  430. $donetext .= ' ('.$done.'/'.$total.') '.get_string('success');
  431. print_progress_redraw($thisbarid, $done, $total, 500, $donetext);
  432. $thisbarid = $starttime = $lasttime = NULL;
  433. }
  434. return;
  435. }
  436. if (empty($starttime)) {
  437. $starttime = $lasttime = time();
  438. $lasttime = $starttime - $updatetime;
  439. $thisbarid = uniqid();
  440. echo '<table width="500" cellpadding="0" cellspacing="0" align="center"><tr><td width="500">';
  441. echo '<div id="bar'.$thisbarid.'" style="border-style:solid;border-width:1px;width:500px;height:50px;">';
  442. echo '<div id="slider'.$thisbarid.'" style="border-style:solid;border-width:1px;height:48px;width:10px;background-color:green;"></div>';
  443. echo '</div>';
  444. echo '<div id="text'.$thisbarid.'" align="center" style="width:500px;"></div>';
  445. echo '</td></tr></table>';
  446. echo '</div>';
  447. }
  448. $now = time();
  449. if ($done && (($now - $lasttime) >= $updatetime)) {
  450. $elapsedtime = $now - $starttime;
  451. $projectedtime = (int)(((float)$total / (float)$done) * $elapsedtime) - $elapsedtime;
  452. $percentage = round((float)$done / (float)$total, 2);
  453. $width = (int)(500 * $percentage);
  454. if ($projectedtime > 10) {
  455. $projectedtext = ' Ending: '.format_time($projectedtime);
  456. } else {
  457. $projectedtext = '';
  458. }
  459. $donetext .= ' ('.$done.'/'.$total.') '.$projectedtext;
  460. print_progress_redraw($thisbarid, $done, $total, $width, $donetext);
  461. $lasttime = $now;
  462. }
  463. }
  464. // Don't call this function directly, it's called from print_progress.
  465. function print_progress_redraw($thisbarid, $done, $total, $width, $donetext='') {
  466. if (empty($thisbarid)) {
  467. return;
  468. }
  469. echo '<script>';
  470. echo 'document.getElementById("text'.$thisbarid.'").innerHTML = "'.addslashes($donetext).'";'."\n";
  471. echo 'document.getElementById("slider'.$thisbarid.'").style.width = \''.$width.'px\';'."\n";
  472. echo '</script>';
  473. }
  474. function upgrade_get_javascript() {
  475. global $CFG;
  476. if (!empty($_SESSION['installautopilot'])) {
  477. $linktoscrolltoerrors = '<script type="text/javascript">var installautopilot = true;</script>'."\n";
  478. } else {
  479. $linktoscrolltoerrors = '<script type="text/javascript">var installautopilot = false;</script>'."\n";
  480. }
  481. $linktoscrolltoerrors .= '<script type="text/javascript" src="' . $CFG->wwwroot . '/lib/scroll_to_errors.js"></script>';
  482. return $linktoscrolltoerrors;
  483. }
  484. function create_admin_user() {
  485. global $CFG, $USER;
  486. if (empty($CFG->rolesactive)) { // No admin user yet.
  487. $user = new object();
  488. $user->auth = 'manual';
  489. $user->firstname = get_string('admin');
  490. $user->lastname = get_string('user');
  491. $user->username = 'admin';
  492. $user->password = hash_internal_user_password('admin');
  493. $user->email = 'root@localhost';
  494. $user->confirmed = 1;
  495. $user->mnethostid = $CFG->mnet_localhost_id;
  496. $user->lang = $CFG->lang;
  497. $user->maildisplay = 1;
  498. $user->timemodified = time();
  499. if (!$user->id = insert_record('user', $user)) {
  500. error('SERIOUS ERROR: Could not create admin user record !!!');
  501. }
  502. if (!$user = get_record('user', 'id', $user->id)) { // Double check.
  503. error('User ID was incorrect (can\'t find it)');
  504. }
  505. // Assign the default admin roles to the new user.
  506. if (!$adminroles = get_roles_with_capability('moodle/legacy:admin', CAP_ALLOW)) {
  507. error('No admin role could be found');
  508. }
  509. $sitecontext = get_context_instance(CONTEXT_SYSTEM);
  510. foreach ($adminroles as $adminrole) {
  511. role_assign($adminrole->id, $user->id, 0, $sitecontext->id);
  512. }
  513. set_config('rolesactive', 1);
  514. // Log the user in.
  515. $USER = get_complete_user_data('username', 'admin');
  516. $USER->newadminuser = 1;
  517. load_all_capabilities();
  518. redirect("$CFG->wwwroot/user/editadvanced.php?id=$user->id"); // Edit thyself
  519. } else {
  520. error('Can not create admin!');
  521. }
  522. }
  523. ////////////////////////////////////////////////
  524. /// upgrade logging functions
  525. ////////////////////////////////////////////////
  526. $upgradeloghandle = false;
  527. $upgradelogbuffer = '';
  528. // I did not find out how to use static variable in callback function,
  529. // the problem was that I could not flush the static buffer :-(
  530. global $upgradeloghandle, $upgradelogbuffer;
  531. /**
  532. * Check if upgrade is already running.
  533. *
  534. * If anything goes wrong due to missing call to upgrade_log_finish()
  535. * just restart the browser.
  536. *
  537. * @param string warning message indicating upgrade is already running
  538. * @param int page reload timeout
  539. */
  540. function upgrade_check_running($message, $timeout) {
  541. if (!empty($_SESSION['upgraderunning'])) {
  542. print_header();
  543. redirect(me(), $message, $timeout);
  544. }
  545. }
  546. /**
  547. * Start logging of output into file (if not disabled) and
  548. * prevent aborting and concurrent execution of upgrade script.
  549. *
  550. * Please note that you can not write into session variables after calling this function!
  551. *
  552. * This function may be called repeatedly.
  553. */
  554. function upgrade_log_start() {
  555. global $CFG, $upgradeloghandle;
  556. if (!empty($_SESSION['upgraderunning'])) {
  557. return; // logging already started
  558. }
  559. @ignore_user_abort(true); // ignore if user stops or otherwise aborts page loading
  560. $_SESSION['upgraderunning'] = 1; // set upgrade indicator
  561. if (empty($CFG->dbsessions)) { // workaround for bug in adodb, db session can not be restarted
  562. session_write_close(); // from now on user can reload page - will be displayed warning
  563. }
  564. make_upload_directory('upgradelogs');
  565. ob_start('upgrade_log_callback', 2); // function for logging to disk; flush each line of text ASAP
  566. register_shutdown_function('upgrade_log_finish'); // in case somebody forgets to stop logging
  567. }
  568. /**
  569. * Terminate logging of output, flush all data, allow script aborting
  570. * and reopen session for writing. Function error() does terminate the logging too.
  571. *
  572. * Please make sure that each upgrade_log_start() is properly terminated by
  573. * this function or error().
  574. *
  575. * This function may be called repeatedly.
  576. */
  577. function upgrade_log_finish() {
  578. global $CFG, $upgradeloghandle, $upgradelogbuffer;
  579. if (empty($_SESSION['upgraderunning'])) {
  580. return; // logging already terminated
  581. }
  582. @ob_end_flush();
  583. if ($upgradelogbuffer !== '') {
  584. @fwrite($upgradeloghandle, $upgradelogbuffer);
  585. $upgradelogbuffer = '';
  586. }
  587. if ($upgradeloghandle and ($upgradeloghandle !== 'error')) {
  588. @fclose($upgradeloghandle);
  589. $upgradeloghandle = false;
  590. }
  591. if (empty($CFG->dbsessions)) {
  592. @session_start(); // ignore header errors, we only need to reopen session
  593. }
  594. $_SESSION['upgraderunning'] = 0; // clear upgrade indicator
  595. if (connection_aborted()) {
  596. die;
  597. }
  598. @ignore_user_abort(false);
  599. }
  600. /**
  601. * Callback function for logging into files. Not more than one file is created per minute,
  602. * upgrade session (terminated by upgrade_log_finish()) is always stored in one file.
  603. *
  604. * This function must not output any characters or throw warnigns and errors!
  605. */
  606. function upgrade_log_callback($string) {
  607. global $CFG, $upgradeloghandle, $upgradelogbuffer;
  608. if (empty($CFG->disableupgradelogging) and ($string != '') and ($upgradeloghandle !== 'error')) {
  609. if ($upgradeloghandle or ($upgradeloghandle = @fopen($CFG->dataroot.'/upgradelogs/upg_'.date('Ymd-Hi').'.html', 'a'))) {
  610. $upgradelogbuffer .= $string;
  611. if (strlen($upgradelogbuffer) > 2048) { // 2kB write buffer
  612. @fwrite($upgradeloghandle, $upgradelogbuffer);
  613. $upgradelogbuffer = '';
  614. }
  615. } else {
  616. $upgradeloghandle = 'error';
  617. }
  618. }
  619. return $string;
  620. }
  621. /**
  622. * Test if and critical warnings are present
  623. * @return bool
  624. */
  625. function admin_critical_warnings_present() {
  626. global $SESSION;
  627. if (!has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM))) {
  628. return 0;
  629. }
  630. if (!isset($SESSION->admin_critical_warning)) {
  631. $SESSION->admin_critical_warning = 0;
  632. if (ini_get_bool('register_globals')) {
  633. $SESSION->admin_critical_warning = 1;
  634. } else if (is_dataroot_insecure(true) === INSECURE_DATAROOT_ERROR) {
  635. $SESSION->admin_critical_warning = 1;
  636. }
  637. }
  638. return $SESSION->admin_critical_warning;
  639. }
  640. /**
  641. * Detects if float support at least 10 deciman digits
  642. * and also if float-->string conversion works as expected.
  643. * @return bool true if problem found
  644. */
  645. function is_float_problem() {
  646. $num1 = 2009010200.01;
  647. $num2 = 2009010200.02;
  648. return ((string)$num1 === (string)$num2 or $num1 === $num2 or $num2 <= (string)$num1);
  649. }
  650. /**
  651. * Try to verify that dataroot is not accessible from web.
  652. * It is not 100% correct but might help to reduce number of vulnerable sites.
  653. *
  654. * Protection from httpd.conf and .htaccess is not detected properly.
  655. * @param bool $fetchtest try to test public access by fetching file
  656. * @return mixed empty means secure, INSECURE_DATAROOT_ERROR found a critical problem, INSECURE_DATAROOT_WARNING migth be problematic
  657. */
  658. function is_dataroot_insecure($fetchtest=false) {
  659. global $CFG;
  660. $siteroot = str_replace('\\', '/', strrev($CFG->dirroot.'/')); // win32 backslash workaround
  661. $rp = preg_replace('|https?://[^/]+|i', '', $CFG->wwwroot, 1);
  662. $rp = strrev(trim($rp, '/'));
  663. $rp = explode('/', $rp);
  664. foreach($rp as $r) {
  665. if (strpos($siteroot, '/'.$r.'/') === 0) {
  666. $siteroot = substr($siteroot, strlen($r)+1); // moodle web in subdirectory
  667. } else {
  668. break; // probably alias root
  669. }
  670. }
  671. $siteroot = strrev($siteroot);
  672. $dataroot = str_replace('\\', '/', $CFG->dataroot.'/');
  673. if (strpos($dataroot, $siteroot) !== 0) {
  674. return false;
  675. }
  676. if (!$fetchtest) {
  677. return INSECURE_DATAROOT_WARNING;
  678. }
  679. // now try all methods to fetch a test file using http protocol
  680. $httpdocroot = str_replace('\\', '/', strrev($CFG->dirroot.'/'));
  681. preg_match('|(https?://[^/]+)|i', $CFG->wwwroot, $matches);
  682. $httpdocroot = $matches[1];
  683. $datarooturl = $httpdocroot.'/'. substr($dataroot, strlen($siteroot));
  684. if (make_upload_directory('diag', false) === false) {
  685. return INSECURE_DATAROOT_WARNING;
  686. }
  687. $testfile = $CFG->dataroot.'/diag/public.txt';
  688. if (!file_exists($testfile)) {
  689. file_put_contents($testfile, 'test file, do not delete');
  690. }
  691. $teststr = trim(file_get_contents($testfile));
  692. if (empty($teststr)) {
  693. // hmm, strange
  694. return INSECURE_DATAROOT_WARNING;
  695. }
  696. $testurl = $datarooturl.'/diag/public.txt';
  697. if (extension_loaded('curl') and
  698. !(stripos(ini_get('disable_functions'), 'curl_init') !== FALSE) and
  699. !(stripos(ini_get('disable_functions'), 'curl_setop') !== FALSE) and
  700. ($ch = @curl_init($testurl)) !== false) {
  701. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  702. curl_setopt($ch, CURLOPT_HEADER, false);
  703. $data = curl_exec($ch);
  704. if (!curl_errno($ch)) {
  705. $data = trim($data);
  706. if ($data === $teststr) {
  707. curl_close($ch);
  708. return INSECURE_DATAROOT_ERROR;
  709. }
  710. }
  711. curl_close($ch);
  712. }
  713. if ($data = @file_get_contents($testurl)) {
  714. $data = trim($data);
  715. if ($data === $teststr) {
  716. return INSECURE_DATAROOT_ERROR;
  717. }
  718. }
  719. preg_match('|https?://([^/]+)|i', $testurl, $matches);
  720. $sitename = $matches[1];
  721. $error = 0;
  722. if ($fp = @fsockopen($sitename, 80, $error)) {
  723. preg_match('|https?://[^/]+(.*)|i', $testurl, $matches);
  724. $localurl = $matches[1];
  725. $out = "GET $localurl HTTP/1.1\r\n";
  726. $out .= "Host: $sitename\r\n";
  727. $out .= "Connection: Close\r\n\r\n";
  728. fwrite($fp, $out);
  729. $data = '';
  730. $incoming = false;
  731. while (!feof($fp)) {
  732. if ($incoming) {
  733. $data .= fgets($fp, 1024);
  734. } else if (@fgets($fp, 1024) === "\r\n") {
  735. $incoming = true;
  736. }
  737. }
  738. fclose($fp);
  739. $data = trim($data);
  740. if ($data === $teststr) {
  741. return INSECURE_DATAROOT_ERROR;
  742. }
  743. }
  744. return INSECURE_DATAROOT_WARNING;
  745. }
  746. /// =============================================================================================================
  747. /// administration tree classes and functions
  748. // n.b. documentation is still in progress for this code
  749. /// INTRODUCTION
  750. /// This file performs the following tasks:
  751. /// -it defines the necessary objects and interfaces to build the Moodle
  752. /// admin hierarchy
  753. /// -it defines the admin_externalpage_setup(), admin_externalpage_print_header(),
  754. /// and admin_externalpage_print_footer() functions used on admin pages
  755. /// ADMIN_SETTING OBJECTS
  756. /// Moodle settings are represented by objects that inherit from the admin_setting
  757. /// class. These objects encapsulate how to read a setting, how to write a new value
  758. /// to a setting, and how to appropriately display the HTML to modify the setting.
  759. /// ADMIN_SETTINGPAGE OBJECTS
  760. /// The admin_setting objects are then grouped into admin_settingpages. The latter
  761. /// appear in the Moodle admin tree block. All interaction with admin_settingpage
  762. /// objects is handled by the admin/settings.php file.
  763. /// ADMIN_EXTERNALPAGE OBJECTS
  764. /// There are some settings in Moodle that are too complex to (efficiently) handle
  765. /// with admin_settingpages. (Consider, for example, user management and displaying
  766. /// lists of users.) In this case, we use the admin_externalpage object. This object
  767. /// places a link to an external PHP file in the admin tree block.
  768. /// If you're using an admin_externalpage object for some settings, you can take
  769. /// advantage of the admin_externalpage_* functions. For example, suppose you wanted
  770. /// to add a foo.php file into admin. First off, you add the following line to
  771. /// admin/settings/first.php (at the end of the file) or to some other file in
  772. /// admin/settings:
  773. /// $ADMIN->add('userinterface', new admin_externalpage('foo', get_string('foo'),
  774. /// $CFG->wwwdir . '/' . '$CFG->admin . '/foo.php', 'some_role_permission'));
  775. /// Next, in foo.php, your file structure would resemble the following:
  776. /// require_once('.../config.php');
  777. /// require_once($CFG->libdir.'/adminlib.php');
  778. /// admin_externalpage_setup('foo');
  779. /// // functionality like processing form submissions goes here
  780. /// admin_externalpage_print_header();
  781. /// // your HTML goes here
  782. /// admin_externalpage_print_footer();
  783. /// The admin_externalpage_setup() function call ensures the user is logged in,
  784. /// and makes sure that they have the proper role permission to access the page.
  785. /// The admin_externalpage_print_header() function prints the header (it figures
  786. /// out what category and subcategories the page is classified under) and ensures
  787. /// that you're using the admin pagelib (which provides the admin tree block and
  788. /// the admin bookmarks block).
  789. /// The admin_externalpage_print_footer() function properly closes the tables
  790. /// opened up by the admin_externalpage_print_header() function and prints the
  791. /// standard Moodle footer.
  792. /// ADMIN_CATEGORY OBJECTS
  793. /// Above and beyond all this, we have admin_category objects. These objects
  794. /// appear as folders in the admin tree block. They contain admin_settingpage's,
  795. /// admin_externalpage's, and other admin_category's.
  796. /// OTHER NOTES
  797. /// admin_settingpage's, admin_externalpage's, and admin_category's all inherit
  798. /// from part_of_admin_tree (a pseudointerface). This interface insists that
  799. /// a class has a check_access method for access permissions, a locate method
  800. /// used to find a specific node in the admin tree and find parent path.
  801. /// admin_category's inherit from parentable_part_of_admin_tree. This pseudo-
  802. /// interface ensures that the class implements a recursive add function which
  803. /// accepts a part_of_admin_tree object and searches for the proper place to
  804. /// put it. parentable_part_of_admin_tree implies part_of_admin_tree.
  805. /// Please note that the $this->name field of any part_of_admin_tree must be
  806. /// UNIQUE throughout the ENTIRE admin tree.
  807. /// The $this->name field of an admin_setting object (which is *not* part_of_
  808. /// admin_tree) must be unique on the respective admin_settingpage where it is
  809. /// used.
  810. /// CLASS DEFINITIONS /////////////////////////////////////////////////////////
  811. /**
  812. * Pseudointerface for anything appearing in the admin tree
  813. *
  814. * The pseudointerface that is implemented by anything that appears in the admin tree
  815. * block. It forces inheriting classes to define a method for checking user permissions
  816. * and methods for finding something in the admin tree.
  817. *
  818. * @author Vincenzo K. Marcovecchio
  819. * @package admin
  820. */
  821. class part_of_admin_tree {
  822. /**
  823. * Finds a named part_of_admin_tree.
  824. *
  825. * Used to find a part_of_admin_tree. If a class only inherits part_of_admin_tree
  826. * and not parentable_part_of_admin_tree, then this function should only check if
  827. * $this->name matches $name. If it does, it should return a reference to $this,
  828. * otherwise, it should return a reference to NULL.
  829. *
  830. * If a class inherits parentable_part_of_admin_tree, this method should be called
  831. * recursively on all child objects (assuming, of course, the parent object's name
  832. * doesn't match the search criterion).
  833. *
  834. * @param string $name The internal name of the part_of_admin_tree we're searching for.
  835. * @return mixed An object reference or a NULL reference.
  836. */
  837. function &locate($name) {
  838. trigger_error('Admin class does not implement method <strong>locate()</strong>', E_USER_WARNING);
  839. return;
  840. }
  841. /**
  842. * Removes named part_of_admin_tree.
  843. *
  844. * @param string $name The internal name of the part_of_admin_tree we want to remove.
  845. * @return bool success.
  846. */
  847. function prune($name) {
  848. trigger_error('Admin class does not implement method <strong>prune()</strong>', E_USER_WARNING);
  849. return;
  850. }
  851. /**
  852. * Search using query
  853. * @param strin query
  854. * @return mixed array-object structure of found settings and pages
  855. */
  856. function search($query) {
  857. trigger_error('Admin class does not implement method <strong>search()</strong>', E_USER_WARNING);
  858. return;
  859. }
  860. /**
  861. * Verifies current user's access to this part_of_admin_tree.
  862. *
  863. * Used to check if the current user has access to this part of the admin tree or
  864. * not. If a class only inherits part_of_admin_tree and not parentable_part_of_admin_tree,
  865. * then this method is usually just a call to has_capability() in the site context.
  866. *
  867. * If a class inherits parentable_part_of_admin_tree, this method should return the
  868. * logical OR of the return of check_access() on all child objects.
  869. *
  870. * @return bool True if the user has access, false if she doesn't.
  871. */
  872. function check_access() {
  873. trigger_error('Admin class does not implement method <strong>check_access()</strong>', E_USER_WARNING);
  874. return;
  875. }
  876. /**
  877. * Mostly usefull for removing of some parts of the tree in admin tree block.
  878. *
  879. * @return True is hidden from normal list view
  880. */
  881. function is_hidden() {
  882. trigger_error('Admin class does not implement method <strong>is_hidden()</strong>', E_USER_WARNING);
  883. return;
  884. }
  885. }
  886. /**
  887. * Pseudointerface implemented by any part_of_admin_tree that has children.
  888. *
  889. * The pseudointerface implemented by any part_of_admin_tree that can be a parent
  890. * to other part_of_admin_tree's. (For now, this only includes admin_category.) Apart
  891. * from ensuring part_of_admin_tree compliancy, it also ensures inheriting methods
  892. * include an add method for adding other part_of_admin_tree objects as children.
  893. *
  894. * @author Vincenzo K. Marcovecchio
  895. * @package admin
  896. */
  897. class parentable_part_of_admin_tree extends part_of_admin_tree {
  898. /**
  899. * Adds a part_of_admin_tree object to the admin tree.
  900. *
  901. * Used to add a part_of_admin_tree object to this object or a child of this
  902. * object. $something should only be added if $destinationname matches
  903. * $this->name. If it doesn't, add should be called on child objects that are
  904. * also parentable_part_of_admin_tree's.
  905. *
  906. * @param string $destinationname The internal name of the new parent for $something.
  907. * @param part_of_admin_tree &$something The object to be added.
  908. * @return bool True on success, false on failure.
  909. */
  910. function add($destinationname, $something) {
  911. trigger_error('Admin class does not implement method <strong>add()</strong>', E_USER_WARNING);
  912. return;
  913. }
  914. }
  915. /**
  916. * The object used to represent folders (a.k.a. categories) in the admin tree block.
  917. *
  918. * Each admin_category object contains a number of part_of_admin_tree objects.
  919. *
  920. * @author Vincenzo K. Marcovecchio
  921. * @package admin
  922. */
  923. class admin_category extends parentable_part_of_admin_tree {
  924. /**
  925. * @var mixed An array of part_of_admin_tree objects that are this object's children
  926. */
  927. var $children;
  928. /**
  929. * @var string An internal name for this category. Must be unique amongst ALL part_of_admin_tree objects
  930. */
  931. var $name;
  932. /**
  933. * @var string The displayed name for this category. Usually obtained through get_string()
  934. */
  935. var $visiblename;
  936. /**
  937. * @var bool Should this category be hidden in admin tree block?
  938. */
  939. var $hidden;
  940. /**
  941. * paths
  942. */
  943. var $path;
  944. var $visiblepath;
  945. /**
  946. * Constructor for an empty admin category
  947. *
  948. * @param string $name The internal name for this category. Must be unique amongst ALL part_of_admin_tree objects
  949. * @param string $visiblename The displayed named for this category. Usually obtained through get_string()
  950. * @param bool $hidden hide category in admin tree block
  951. */
  952. function admin_category($name, $visiblename, $hidden=false) {
  953. $this->children = array();
  954. $this->name = $name;
  955. $this->visiblename = $visiblename;
  956. $this->hidden = $hidden;
  957. }
  958. /**
  959. * Returns a reference to the part_of_admin_tree object with internal name $name.
  960. *
  961. * @param string $name The internal name of the object we want.
  962. * @param bool $findpath initialize path and visiblepath arrays
  963. * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL.
  964. */
  965. function &locate($name, $findpath=false) {
  966. if ($this->name == $name) {
  967. if ($findpath) {
  968. $this->visiblepath[] = $this->visiblename;
  969. $this->path[] = $this->name;
  970. }
  971. return $this;
  972. }
  973. $return = NULL;
  974. foreach($this->children as $childid=>$unused) {
  975. if ($return =& $this->children[$childid]->locate($name, $findpath)) {
  976. break;
  977. }
  978. }
  979. if (!is_null($return) and $findpath) {
  980. $return->visiblepath[] = $this->visiblename;
  981. $return->path[] = $this->name;
  982. }
  983. return $return;
  984. }
  985. /**
  986. * Search using query
  987. * @param strin query
  988. * @return mixed array-object structure of found settings and pages
  989. */
  990. function search($query) {
  991. $result = array();
  992. foreach ($this->children as $child) {
  993. $subsearch = $child->search($query);
  994. if (!is_array($subsearch)) {
  995. debugging('Incorrect search result from '.$child->name);
  996. continue;
  997. }
  998. $result = array_merge($result, $subsearch);
  999. }
  1000. return $result;
  1001. }
  1002. /**
  1003. * Removes part_of_admin_tree object with internal name $name.
  1004. *
  1005. * @param string $name The internal name of the object we want to remove.
  1006. * @return bool success
  1007. */
  1008. function prune($name) {
  1009. if ($this->name == $name) {
  1010. return false; //can not remove itself
  1011. }
  1012. foreach($this->children as $precedence => $child) {
  1013. if ($child->name == $name) {
  1014. // found it!
  1015. unset($this->children[$precedence]);
  1016. return true;
  1017. }
  1018. if ($this->children[$precedence]->prune($name)) {
  1019. return true;
  1020. }
  1021. }
  1022. return false;
  1023. }
  1024. /**
  1025. * Adds a part_of_admin_tree to a child or grandchild (or great-grandchild, and so forth) of this object.
  1026. *
  1027. * @param string $destinationame The internal name of the immediate parent that we want for $something.
  1028. * @param mixed $something A part_of_admin_tree or setting instanceto be added.
  1029. * @return bool True if successfully added, false if $something can not be added.
  1030. */
  1031. function add($parentname, $something) {
  1032. $parent =& $this->locate($parentname);
  1033. if (is_null($parent)) {
  1034. debugging('parent does not exist!');
  1035. return false;
  1036. }
  1037. if (is_a($something, 'part_of_admin_tree')) {
  1038. if (!is_a($parent, 'parentable_part_of_admin_tree')) {
  1039. debugging('error - parts of tree can be inserted only into parentable parts');
  1040. return false;
  1041. }
  1042. $parent->children[] = $something;
  1043. return true;
  1044. } else {
  1045. debugging('error - can not add this element');
  1046. return false;
  1047. }
  1048. }
  1049. /**
  1050. * Checks if the user has access to anything in this category.
  1051. *
  1052. * @return bool True if the user has access to atleast one child in this category, false otherwise.
  1053. */
  1054. function check_access() {
  1055. foreach ($this->children as $child) {
  1056. if ($child->check_access()) {
  1057. return true;
  1058. }
  1059. }
  1060. return false;
  1061. }
  1062. /**
  1063. * Is this category hidden in admin tree block?
  1064. *
  1065. * @return bool True if hidden
  1066. */
  1067. function is_hidden() {
  1068. return $this->hidden;
  1069. }
  1070. }
  1071. class admin_root extends admin_category {
  1072. /**
  1073. * list of errors
  1074. */
  1075. var $errors;
  1076. /**
  1077. * search query
  1078. */
  1079. var $search;
  1080. /**
  1081. * full tree flag - true means all settings required, false onlypages required
  1082. */
  1083. var $fulltree;
  1084. function admin_root() {
  1085. parent::admin_category('root', get_string('administration'), false);
  1086. $this->errors = array();
  1087. $this->search = '';
  1088. $this->fulltree = true;
  1089. }
  1090. }
  1091. /**
  1092. * Links external PHP pages into the admin tree.
  1093. *
  1094. * See detailed usage example at the top of this document (adminlib.php)
  1095. *
  1096. * @author Vincenzo K. Marcovecchio
  1097. * @package admin
  1098. */
  1099. class admin_externalpage extends part_of_admin_tree {
  1100. /**
  1101. * @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects
  1102. */
  1103. var $name;
  1104. /**
  1105. * @var string The displayed name for this external page. Usually obtained through get_string().
  1106. */
  1107. var $visiblename;
  1108. /**
  1109. * @var string The external URL that we should link to when someone requests this external page.
  1110. */
  1111. var $url;
  1112. /**
  1113. * @var string The role capability/permission a user must have to access this external page.
  1114. */
  1115. var $req_capability;
  1116. /**
  1117. * @var object The context in which capability/permission should be checked, default is site context.
  1118. */
  1119. var $context;
  1120. /**
  1121. * @var bool hidden in admin tree block.
  1122. */
  1123. var $hidden;
  1124. /**
  1125. * visible path
  1126. */
  1127. var $path;
  1128. var $visiblepath;
  1129. /**
  1130. * Constructor for adding an external page into the admin tree.
  1131. *
  1132. * @param str…

Large files files are truncated, but you can click here to view the full file