MDL-18011 - removing reference to loancalc block from core
[moodle.git] / lib / db / upgradelib.php
CommitLineData
117bd748 1<?php
42ff9ce6 2
3/*
4 * This file is used for special upgrade functions - for example groups and gradebook.
56a1a882 5 * These functions must use SQL and database related functions only- no other Moodle API,
42ff9ce6 6 * because it might depend on db structures that are not yet present during upgrade.
7 * (Do not use functions from accesslib.php, grades classes or group functions at all!)
8 */
9
863850d2 10function upgrade_fix_category_depths() {
f33e1ed4 11 global $CFG, $DB;
863850d2 12
13 // first fix incorrect parents
14 $sql = "SELECT c.id
f33e1ed4 15 FROM {course_categories} c
16 WHERE c.parent > 0 AND c.parent NOT IN (SELECT pc.id FROM {course_categories} pc)";
17 if ($rs = $DB->get_recordset_sql($sql)) {
18 foreach ($rs as $cat) {
863850d2 19 $cat->depth = 1;
20 $cat->path = '/'.$cat->id;
21 $cat->parent = 0;
f33e1ed4 22 $DB->update_record('course_categories', $cat);
863850d2 23 }
f33e1ed4 24 $rs->close();
863850d2 25 }
26
27 // now add path and depth to top level categories
f33e1ed4 28 $sql = "UPDATE {course_categories}
245ac557 29 SET depth = 1, path = ".$DB->sql_concat("'/'", "id")."
863850d2 30 WHERE parent = 0";
f33e1ed4 31 $DB->execute($sql);
863850d2 32
33 // now fix all other levels - slow but works in all supported dbs
34 $parentdepth = 1;
f33e1ed4 35 while ($DB->record_exists('course_categories', array('depth'=>0))) {
863850d2 36 $sql = "SELECT c.id, pc.path
f33e1ed4 37 FROM {course_categories} c, {course_categories} pc
38 WHERE c.parent=pc.id AND c.depth=0 AND pc.depth=?";
39 if ($rs = $DB->get_recordset_sql($sql, array($parentdepth))) {
f33e1ed4 40 foreach ($rs as $cat) {
863850d2 41 $cat->depth = $parentdepth+1;
42 $cat->path = $cat->path.'/'.$cat->id;
f33e1ed4 43 $DB->update_record('course_categories', $cat);
863850d2 44 }
f33e1ed4 45 $rs->close();
863850d2 46 }
47 $parentdepth++;
48 if ($parentdepth > 100) {
49 //something must have gone wrong - nobody can have more than 100 levels of categories, right?
50 debugging('Unknown error fixing category depths');
51 break;
52 }
53 }
863850d2 54}
55
172dd12c 56/**
57 * Moves all course files except the moddata to new file storage
58 *
59 * Unfortunately this function uses core file related functions - it might be necessary to tweak it if something changes there :-(
60 */
61function upgrade_migrate_files_courses() {
62 global $DB, $CFG;
63 require_once($CFG->libdir.'/filelib.php');
64
65 $count = $DB->count_records('course');
66 $pbar = new progress_bar('migratecoursefiles', 500, true);
67
68 $rs = $DB->get_recordset('course');
172dd12c 69 $i = 0;
70 foreach ($rs as $course) {
71 $i++;
775f811a 72 upgrade_set_timeout(60*5); // set up timeout, may also abort execution
172dd12c 73 $context = get_context_instance(CONTEXT_COURSE, $course->id);
74 upgrade_migrate_files_course($context, '/', true);
75 $pbar->update($i, $count, "Migrated course files - course $i/$count.");
76 }
172dd12c 77 $rs->close();
78
79 return true;
80}
81
82/**
83 * Internal function - do not use directly
84 */
85function upgrade_migrate_files_course($context, $path, $delete) {
aa9a6867 86 global $CFG, $OUTPUT;
172dd12c 87
88 $fullpathname = $CFG->dataroot.'/'.$context->instanceid.$path;
89 if (!file_exists($fullpathname)) {
90 return;
91 }
92 $items = new DirectoryIterator($fullpathname);
93 $fs = get_file_storage();
94
95 foreach ($items as $item) {
96 if ($item->isDot()) {
97 continue;
98 }
99
100 if ($item->isLink()) {
775f811a 101 // do not delete symbolic links or its children
172dd12c 102 $delete_this = false;
103 } else {
104 $delete_this = $delete;
105 }
106
107 if (strpos($path, '/backupdata/') === 0) {
108 $filearea = 'course_backup';
109 $filepath = substr($path, strlen('/backupdata'));
110 } else {
111 $filearea = 'course_content';
112 $filepath = $path;
113 }
114
115 if ($item->isFile()) {
116 if (!$item->isReadable()) {
aa9a6867 117 echo $OUTPUT->notification(" File not readable, skipping: ".$fullpathname.$item->getFilename());
172dd12c 118 continue;
119 }
120
121 $filepath = clean_param($filepath, PARAM_PATH);
122 $filename = clean_param($item->getFilename(), PARAM_FILE);
123
124 if ($filename === '') {
aa54ed7b 125 //unsupported chars, sorry
172dd12c 126 continue;
127 }
128
129 if (!$fs->file_exists($context->id, $filearea, '0', $filepath, $filename)) {
130 $file_record = array('contextid'=>$context->id, 'filearea'=>$filearea, 'itemid'=>0, 'filepath'=>$filepath, 'filename'=>$filename,
131 'timecreated'=>$item->getCTime(), 'timemodified'=>$item->getMTime());
132 if ($fs->create_file_from_pathname($file_record, $fullpathname.$item->getFilename())) {
133 if ($delete_this) {
134 @unlink($fullpathname.$item->getFilename());
135 }
136 }
137 }
138
139 } else {
140 if ($path == '/' and $item->getFilename() == 'moddata') {
141 continue; // modules are responsible
142 }
143
aa54ed7b 144 $dirname = clean_param($item->getFilename(), PARAM_PATH);
145 if ($dirname === '') {
146 //unsupported chars, sorry
147 continue;
148 }
149 $filepath = ($filepath.$dirname.'/');
172dd12c 150 if ($filepath !== '/backupdata/') {
151 $fs->create_directory($context->id, $filearea, 0, $filepath);
152 }
153
154 //migrate recursively all subdirectories
155 upgrade_migrate_files_course($context, $path.$item->getFilename().'/', $delete_this);
156 if ($delete_this) {
157 // delete dir if empty
158 @rmdir($fullpathname.$item->getFilename());
159 }
160 }
161 }
162 unset($items); //release file handles
163}
164
165/**
166 * Moves all block attachments
167 *
168 * Unfortunately this function uses core file related functions - it might be necessary to tweak it if something changes there :-(
169 */
170function upgrade_migrate_files_blog() {
aa9a6867 171 global $DB, $CFG, $OUTPUT;
172dd12c 172
173 $fs = get_file_storage();
174
a1442d4e 175 $count = $DB->count_records_select('post', "module='blog' AND attachment IS NOT NULL AND attachment <> '1'");
172dd12c 176
a1442d4e 177 if ($rs = $DB->get_recordset_select('post', "module='blog' AND attachment IS NOT NULL AND attachment <> '1'")) {
172dd12c 178
775f811a 179 upgrade_set_timeout(60*20); // set up timeout, may also abort execution
180
172dd12c 181 $pbar = new progress_bar('migrateblogfiles', 500, true);
182
172dd12c 183 $i = 0;
184 foreach ($rs as $entry) {
185 $i++;
186 $pathname = "$CFG->dataroot/blog/attachments/$entry->id/$entry->attachment";
187 if (!file_exists($pathname)) {
775f811a 188 $entry->attachment = NULL;
189 $DB->update_record('post', $entry);
172dd12c 190 continue;
191 }
192
193 $filename = clean_param($entry->attachment, PARAM_FILE);
194 if ($filename === '') {
195 // weird file name, ignore it
196 $entry->attachment = NULL;
197 $DB->update_record('post', $entry);
198 continue;
199 }
200
201 if (!is_readable($pathname)) {
aa9a6867 202 echo $OUTPUT->notification(" File not readable, skipping: ".$pathname);
172dd12c 203 continue;
204 }
205
206 if (!$fs->file_exists(SYSCONTEXTID, 'blog', $entry->id, '/', $filename)) {
cc82aef6 207 $file_record = array('contextid'=>SYSCONTEXTID, 'filearea'=>'blog_attachment', 'itemid'=>$entry->id, 'filepath'=>'/', 'filename'=>$filename,
775f811a 208 'timecreated'=>filectime($pathname), 'timemodified'=>filemtime($pathname), 'userid'=>$entry->userid);
172dd12c 209 $fs->create_file_from_pathname($file_record, $pathname);
210 }
211 @unlink($pathname);
212 @rmdir("$CFG->dataroot/blog/attachments/$entry->id/");
213
214 $entry->attachment = 1; // file name not needed there anymore
215 $DB->update_record('post', $entry);
216 $pbar->update($i, $count, "Migrated blog attachments - $i/$count.");
217 }
172dd12c 218 $rs->close();
219 }
775f811a 220
172dd12c 221 @rmdir("$CFG->dataroot/blog/attachments/");
222 @rmdir("$CFG->dataroot/blog/");
223}
02caf1e7 224
225/**
226 * This function will fix the status of the localhost/all records in the mnet_host table
227 * checking they exist and adding them if missing + redefine CFG->mnet_localhost_id and
228 * CFG->mnet_all_hosts_id if needed + update all the users having non-existent mnethostid
229 * to correct CFG->mnet_localhost_id
230 *
231 * Implemented because, at some point, specially in old installations upgraded along
232 * multiple versions, sometimes the stuff above has ended being inconsistent, causing
233 * problems here and there (noticeablely in backup/restore). MDL-16879
234 */
235function upgrade_fix_incorrect_mnethostids() {
236
237 global $CFG, $DB;
238
239/// Get current $CFG/mnet_host records
240 $old_mnet_localhost_id = !empty($CFG->mnet_localhost_id) ? $CFG->mnet_localhost_id : 0;
241 $old_mnet_all_hosts_id = !empty($CFG->mnet_all_hosts_id) ? $CFG->mnet_all_hosts_id : 0;
242
243 $current_mnet_localhost_host = $DB->get_record('mnet_host', array('wwwroot' => $CFG->wwwroot)); /// By wwwroot
244 $current_mnet_all_hosts_host = $DB->get_record_select('mnet_host', $DB->sql_isempty('mnet_host', 'wwwroot', false, false)); /// By empty wwwroot
245
7062a798
PL
246 if (!$moodleapplicationid = $DB->get_field('mnet_application', 'id', array('name' => 'moodle'))) {
247 $m = (object)array(
248 'name' => 'moodle',
249 'display_name' => 'Moodle',
250 'xmlrpc_server_url' => '/mnet/xmlrpc/server.php',
251 'sso_land_url' => '/auth/mnet/land.php',
c9606565 252 'sso_jump_url' => '/auth/mnet/jump.php',
7062a798
PL
253 );
254 $moodleapplicationid = $DB->insert_record('mnet_application', $m);
255 }
256
02caf1e7 257/// Create localhost_host if necessary (pretty improbable but better to be 100% in the safe side)
258/// Code stolen from mnet_environment->init
259 if (!$current_mnet_localhost_host) {
260 $current_mnet_localhost_host = new stdClass();
261 $current_mnet_localhost_host->wwwroot = $CFG->wwwroot;
262 $current_mnet_localhost_host->ip_address = '';
263 $current_mnet_localhost_host->public_key = '';
264 $current_mnet_localhost_host->public_key_expires = 0;
265 $current_mnet_localhost_host->last_connect_time = 0;
266 $current_mnet_localhost_host->last_log_id = 0;
267 $current_mnet_localhost_host->deleted = 0;
268 $current_mnet_localhost_host->name = '';
7062a798 269 $current_mnet_localhost_host->applicationid = $moodleapplicationid;
02caf1e7 270 /// Get the ip of the server
271 if (empty($_SERVER['SERVER_ADDR'])) {
272 /// SERVER_ADDR is only returned by Apache-like webservers
273 $count = preg_match("@^(?:http[s]?://)?([A-Z0-9\-\.]+).*@i", $current_mnet_localhost_host->wwwroot, $matches);
274 $my_hostname = $count > 0 ? $matches[1] : false;
275 $my_ip = gethostbyname($my_hostname); // Returns unmodified hostname on failure. DOH!
276 if ($my_ip == $my_hostname) {
277 $current_mnet_localhost_host->ip_address = 'UNKNOWN';
278 } else {
279 $current_mnet_localhost_host->ip_address = $my_ip;
280 }
281 } else {
282 $current_mnet_localhost_host->ip_address = $_SERVER['SERVER_ADDR'];
283 }
284 $current_mnet_localhost_host->id = $DB->insert_record('mnet_host', $current_mnet_localhost_host, true);
285 }
286
287/// Create all_hosts_host if necessary (pretty improbable but better to be 100% in the safe side)
288/// Code stolen from mnet_environment->init
289 if (!$current_mnet_all_hosts_host) {
290 $current_mnet_all_hosts_host = new stdClass();
291 $current_mnet_all_hosts_host->wwwroot = '';
292 $current_mnet_all_hosts_host->ip_address = '';
293 $current_mnet_all_hosts_host->public_key = '';
294 $current_mnet_all_hosts_host->public_key_expires = 0;
295 $current_mnet_all_hosts_host->last_connect_time = 0;
296 $current_mnet_all_hosts_host->last_log_id = 0;
297 $current_mnet_all_hosts_host->deleted = 0;
298 $current_mnet_all_hosts_host->name = 'All Hosts';
7062a798 299 $current_mnet_all_hosts_host->applicationid = $moodleapplicationid;
02caf1e7 300 $current_mnet_all_hosts_host->id = $DB->insert_record('mnet_host', $current_mnet_all_hosts_host, true);
301 }
302
303/// Compare old_mnet_localhost_id and current_mnet_localhost_host
304
305 if ($old_mnet_localhost_id != $current_mnet_localhost_host->id) { /// Different = problems
306 /// Update $CFG->mnet_localhost_id to correct value
307 set_config('mnet_localhost_id', $current_mnet_localhost_host->id);
308
309 /// Delete $old_mnet_localhost_id if exists (users will be assigned to new one below)
310 $DB->delete_records('mnet_host', array('id' => $old_mnet_localhost_id));
311 }
312
313/// Compare old_mnet_all_hosts_id and current_mnet_all_hosts_host
314
315 if ($old_mnet_all_hosts_id != $current_mnet_all_hosts_host->id) { /// Different = problems
316 /// Update $CFG->mnet_localhost_id to correct value
317 set_config('mnet_all_hosts_id', $current_mnet_all_hosts_host->id);
318
319 /// Delete $old_mnet_all_hosts_id if exists
320 $DB->delete_records('mnet_host', array('id' => $old_mnet_all_hosts_id));
321 }
322
323/// Finally, update all the incorrect user->mnethostid to the correct CFG->mnet_localhost_id, preventing UIX dupes
324 $hosts = $DB->get_records_menu('mnet_host', null, '', 'id, id AS id2');
325 list($in_sql, $in_params) = $DB->get_in_or_equal($hosts, SQL_PARAMS_QM, null, false);
326
327 $sql = "SELECT id
ba73136e 328 FROM {user} u1
02caf1e7 329 WHERE u1.mnethostid $in_sql
330 AND NOT EXISTS (
331 SELECT 'x'
ba73136e 332 FROM {user} u2
02caf1e7 333 WHERE u2.username = u1.username
334 AND u2.mnethostid = ?)";
335
336 $params = array_merge($in_params, array($current_mnet_localhost_host->id));
337
338 if ($rs = $DB->get_recordset_sql($sql, $params)) {
339 foreach ($rs as $rec) {
340 $DB->set_field('user', 'mnethostid', $current_mnet_localhost_host->id, array('id' => $rec->id));
341 upgrade_set_timeout(60); /// Give upgrade at least 60 more seconds
342 }
343 $rs->close();
344 }
7062a798
PL
345
346 // fix up any host records that have incorrect ids
347 $DB->set_field_select('mnet_host', 'applicationid', $moodleapplicationid, 'id = ? or id = ?', array($current_mnet_localhost_host->id, $current_mnet_all_hosts_host->id));
348
02caf1e7 349}
350
7d2a0492 351/**
352 * This function is used as part of the great navigation upgrade of 20090828
353 * It is used to clean up contexts that are unique to a blocks that are about
354 * to be removed.
355 *
356 *
357 * Look at {@link blocklib.php::blocks_delete_instance()} the function from
358 * which I based this code. It is important to mention one very important fact
359 * before doing this I checked that the blocks did not override the
360 * {@link block_base::instance_delete()} method. Should this function ever
361 * be repeated check this again
117bd748 362 *
7d2a0492 363 * @link lib/db/upgrade.php
364 *
365 * @since navigation upgrade 20090828
366 * @param array $contextidarray An array of block instance context ids
61676608 367 * @return void
7d2a0492 368 */
369function upgrade_cleanup_unwanted_block_contexts($contextidarray) {
370 global $DB;
371
372 if (!is_array($contextidarray) || count($contextidarray)===0) {
373 // Ummmm no instances?
61676608 374 return;
7d2a0492 375 }
376
377 $contextidstring = join(',', $contextidarray);
378
379 $blockcontexts = $DB->get_recordset_select('context', 'contextlevel = '.CONTEXT_BLOCK.' AND id IN ('.$contextidstring.')', array(), '', 'id, contextlevel');
380 $blockcontextids = array();
381 foreach ($blockcontexts as $blockcontext) {
382 $blockcontextids[] = $blockcontext->id;
383 }
384
385 if (count($blockcontextids)===0) {
386 // None of the instances have unique contexts
61676608 387 return;
7d2a0492 388 }
389
390 $blockcontextidsstring = join(',', $blockcontextids);
391
61676608
PS
392 $DB->delete_records_select('role_assignments', 'contextid IN ('.$blockcontextidsstring.')');
393 $DB->delete_records_select('role_capabilities', 'contextid IN ('.$blockcontextidsstring.')');
394 $DB->delete_records_select('role_names', 'contextid IN ('.$blockcontextidsstring.')');
395 $DB->delete_records_select('context', 'id IN ('.$blockcontextidsstring.')');
67e4713c
AD
396}
397
398/**
399 * Shifts ratings from modules into the central rating table
400 *
401 * @param string $ratingssql sql to return the module's rating
402 * @param string $modulename the name of the module
403 * @return boolean success flag
404 */
63e87951 405/*function upgrade_module_ratings($ratingssql, $modulename) {
67e4713c
AD
406 global $DB;
407 $contextid = null;
408 $contextarray = array();
409 $result = true;
410 $i=0;
411
412 $ratings = $DB->get_recordset_sql($ratingssql);
413
414 foreach ($ratings as $old_rating) {
415 if($i++%500==0) {
416 upgrade_set_timeout(60);//prevent a timeout
417 }
418
419 //all posts within a given forum, glossary etc will have the same context id so store them in an array
420 if( !array_key_exists($old_rating->mid, $contextarray) ) {
63e87951 421 $sql = "SELECT cxt.id
67e4713c
AD
422 FROM {context} cxt
423 JOIN {course_modules} cm ON cm.id = cxt.instanceid
424 JOIN {modules} m ON cm.module = m.id
425 WHERE m.name = :modulename AND cm.instance = :moduleinstanceid AND cxt.contextlevel = :contextmodule";
426 $params = array();
63e87951 427 $params['modulename'] = $modulename;
67e4713c 428 $params['moduleinstanceid'] = $old_rating->mid;
63e87951 429 $params['contextmodule'] = CONTEXT_MODULE;
67e4713c
AD
430 $results = $DB->get_record_sql($sql, $params);
431 $contextarray[$old_rating->mid] = $results->id;
432 }
433 $contextid = $contextarray[$old_rating->mid];
434
435 $rating = new stdclass();
436 $rating->contextid = $contextid;
437 $rating->scaleid = $old_rating->scale;
438 $rating->itemid = $old_rating->itemid;
439 $rating->rating = $old_rating->rating;
440 $rating->userid = $old_rating->userid;
441 $rating->timecreated = $old_rating->timecreated;
442 $rating->timemodified = $old_rating->timemodified;
443
444 if( !$DB->insert_record('rating', $rating) ) {
63e87951
AD
445 return false;
446 }
67e4713c
AD
447 }
448
449 $ratings->close();
450
451 return true;
63e87951 452}*/