MDL-13766, fixed repository instance name and configplugin language string
[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
edfd6a5e
PS
82/**
83 * Internal function - do not use directly
84 */
85function upgrade_migrate_user_icons() {
86 global $CFG, $OUTPUT, $DB;
87
88 $fs = get_file_storage();
89
90 $icon = array('component'=>'user', 'filearea'=>'icon', 'itemid'=>0, 'filepath'=>'/');
91
92 $count = $DB->count_records('user', array('picture'=>1, 'deleted'=>0));
93 $pbar = new progress_bar('migratecoursefiles', 500, true);
94
95 $rs = $DB->get_recordset('user', array('picture'=>1, 'deleted'=>0), 'id ASC', 'id, picture');
96 $i = 0;
97 foreach ($rs as $user) {
98 $i++;
99 upgrade_set_timeout(60); /// Give upgrade at least 60 more seconds
e88dd876 100 $pbar->update($i, $count, "Migrated user icons $i/$count.");
edfd6a5e
PS
101
102 $context = get_context_instance(CONTEXT_USER, $user->id);
103
104 if ($fs->file_exists($context->id, 'user', 'icon', 0, '/', 'f1.jpg')) {
105 // already converted!
106 continue;
107 }
108
109 $level1 = floor($user->id / 1000) * 1000;
110 $userdir = "$CFG->dataroot/user/$level1/$user->id";
111 if (!file_exists("$userdir/f1.jpg") or !file_exists("$userdir/f2.jpg")) {
112 $userdir = "$CFG->dataroot/users/$user->id";
113 if (!file_exists("$userdir/f1.jpg") or !file_exists("$userdir/f2.jpg")) {
114 // no image found, sorry
115 $user->picture = 0;
116 $DB->update_record('user', $user);
117 continue;
118 }
119 }
120
121 $icon['contextid'] = $context->id;
122 $icon['filename'] = 'f1.jpg';
123 $fs->create_file_from_pathname($icon, "$userdir/f1.jpg");
124 $icon['filename'] = 'f2.jpg';
125 $fs->create_file_from_pathname($icon, "$userdir/f2.jpg");
126 }
127 $rs->close();
128
129 // purge all old user image dirs
c7744f12
PS
130 remove_dir("$CFG->dataroot/user");
131 remove_dir("$CFG->dataroot/users");
edfd6a5e
PS
132}
133
e88dd876
PS
134/**
135 * Internal function - do not use directly
136 */
137function upgrade_migrate_group_icons() {
138 global $CFG, $OUTPUT, $DB;
139
140 $fs = get_file_storage();
141
142 $icon = array('component'=>'group', 'filearea'=>'icon', 'filepath'=>'/');
143
144 $count = $DB->count_records('groups', array('picture'=>1));
145 $pbar = new progress_bar('migrategroupfiles', 500, true);
146
147 $rs = $DB->get_recordset('groups', array('picture'=>1), 'courseid ASC', 'id, picture, courseid');
148 $i = 0;
149 foreach ($rs as $group) {
150 $i++;
151 upgrade_set_timeout(60); /// Give upgrade at least 60 more seconds
152 $pbar->update($i, $count, "Migrated group icons $i/$count.");
153
154 $context = get_context_instance(CONTEXT_COURSE, $group->courseid);
155
156 if ($fs->file_exists($context->id, 'group', 'icon', $group->id, '/', 'f1.jpg')) {
157 // already converted!
158 continue;
159 }
160
161 $groupdir = "$CFG->dataroot/groups/$group->id";
162 if (!file_exists("$groupdir/f1.jpg") or !file_exists("$groupdir/f2.jpg")) {
163 // no image found, sorry
164 $group->picture = 0;
165 $DB->update_record('groups', $group);
166 continue;
167 }
168
169 $icon['contextid'] = $context->id;
170 $icon['itemid'] = $group->id;
171 $icon['filename'] = 'f1.jpg';
172 $fs->create_file_from_pathname($icon, "$groupdir/f1.jpg");
173 $icon['filename'] = 'f2.jpg';
174 $fs->create_file_from_pathname($icon, "$groupdir/f2.jpg");
175 }
176 $rs->close();
177
178 // purge all old group image dirs
179 remove_dir("$CFG->dataroot/groups");
180}
181
172dd12c 182/**
183 * Internal function - do not use directly
184 */
185function upgrade_migrate_files_course($context, $path, $delete) {
aa9a6867 186 global $CFG, $OUTPUT;
172dd12c 187
188 $fullpathname = $CFG->dataroot.'/'.$context->instanceid.$path;
189 if (!file_exists($fullpathname)) {
190 return;
191 }
192 $items = new DirectoryIterator($fullpathname);
193 $fs = get_file_storage();
194
195 foreach ($items as $item) {
196 if ($item->isDot()) {
197 continue;
198 }
199
200 if ($item->isLink()) {
775f811a 201 // do not delete symbolic links or its children
172dd12c 202 $delete_this = false;
203 } else {
204 $delete_this = $delete;
205 }
206
207 if (strpos($path, '/backupdata/') === 0) {
64f93798
PS
208 $component = 'backup';
209 $filearea = 'course';
210 $filepath = substr($path, strlen('/backupdata'));
172dd12c 211 } else {
64f93798
PS
212 $component = 'course';
213 $filearea = 'legacy';
214 $filepath = $path;
172dd12c 215 }
216
217 if ($item->isFile()) {
218 if (!$item->isReadable()) {
aa9a6867 219 echo $OUTPUT->notification(" File not readable, skipping: ".$fullpathname.$item->getFilename());
172dd12c 220 continue;
221 }
222
223 $filepath = clean_param($filepath, PARAM_PATH);
224 $filename = clean_param($item->getFilename(), PARAM_FILE);
225
226 if ($filename === '') {
aa54ed7b 227 //unsupported chars, sorry
172dd12c 228 continue;
229 }
230
64f93798
PS
231 if (!$fs->file_exists($context->id, $component, $filearea, '0', $filepath, $filename)) {
232 $file_record = array('contextid'=>$context->id, 'component'=>$component, 'filearea'=>$filearea, 'itemid'=>0, 'filepath'=>$filepath, 'filename'=>$filename,
172dd12c 233 'timecreated'=>$item->getCTime(), 'timemodified'=>$item->getMTime());
234 if ($fs->create_file_from_pathname($file_record, $fullpathname.$item->getFilename())) {
235 if ($delete_this) {
236 @unlink($fullpathname.$item->getFilename());
237 }
238 }
239 }
240
241 } else {
242 if ($path == '/' and $item->getFilename() == 'moddata') {
243 continue; // modules are responsible
244 }
245
aa54ed7b 246 $dirname = clean_param($item->getFilename(), PARAM_PATH);
247 if ($dirname === '') {
248 //unsupported chars, sorry
249 continue;
250 }
251 $filepath = ($filepath.$dirname.'/');
172dd12c 252 if ($filepath !== '/backupdata/') {
64f93798 253 $fs->create_directory($context->id, $component, $filearea, 0, $filepath);
172dd12c 254 }
255
256 //migrate recursively all subdirectories
257 upgrade_migrate_files_course($context, $path.$item->getFilename().'/', $delete_this);
258 if ($delete_this) {
259 // delete dir if empty
260 @rmdir($fullpathname.$item->getFilename());
261 }
262 }
263 }
264 unset($items); //release file handles
265}
266
267/**
268 * Moves all block attachments
269 *
270 * Unfortunately this function uses core file related functions - it might be necessary to tweak it if something changes there :-(
271 */
272function upgrade_migrate_files_blog() {
aa9a6867 273 global $DB, $CFG, $OUTPUT;
172dd12c 274
275 $fs = get_file_storage();
276
a1442d4e 277 $count = $DB->count_records_select('post', "module='blog' AND attachment IS NOT NULL AND attachment <> '1'");
172dd12c 278
a1442d4e 279 if ($rs = $DB->get_recordset_select('post', "module='blog' AND attachment IS NOT NULL AND attachment <> '1'")) {
172dd12c 280
775f811a 281 upgrade_set_timeout(60*20); // set up timeout, may also abort execution
282
172dd12c 283 $pbar = new progress_bar('migrateblogfiles', 500, true);
284
172dd12c 285 $i = 0;
286 foreach ($rs as $entry) {
287 $i++;
288 $pathname = "$CFG->dataroot/blog/attachments/$entry->id/$entry->attachment";
289 if (!file_exists($pathname)) {
775f811a 290 $entry->attachment = NULL;
291 $DB->update_record('post', $entry);
172dd12c 292 continue;
293 }
294
295 $filename = clean_param($entry->attachment, PARAM_FILE);
296 if ($filename === '') {
297 // weird file name, ignore it
298 $entry->attachment = NULL;
299 $DB->update_record('post', $entry);
300 continue;
301 }
302
303 if (!is_readable($pathname)) {
aa9a6867 304 echo $OUTPUT->notification(" File not readable, skipping: ".$pathname);
172dd12c 305 continue;
306 }
307
64f93798
PS
308 if (!$fs->file_exists(SYSCONTEXTID, 'blog', 'attachment', $entry->id, '/', $filename)) {
309 $file_record = array('contextid'=>SYSCONTEXTID, 'component'=>'blog', 'filearea'=>'attachment', 'itemid'=>$entry->id, 'filepath'=>'/', 'filename'=>$filename,
775f811a 310 'timecreated'=>filectime($pathname), 'timemodified'=>filemtime($pathname), 'userid'=>$entry->userid);
172dd12c 311 $fs->create_file_from_pathname($file_record, $pathname);
312 }
313 @unlink($pathname);
314 @rmdir("$CFG->dataroot/blog/attachments/$entry->id/");
315
316 $entry->attachment = 1; // file name not needed there anymore
317 $DB->update_record('post', $entry);
318 $pbar->update($i, $count, "Migrated blog attachments - $i/$count.");
319 }
172dd12c 320 $rs->close();
321 }
775f811a 322
172dd12c 323 @rmdir("$CFG->dataroot/blog/attachments/");
324 @rmdir("$CFG->dataroot/blog/");
325}
02caf1e7 326
327/**
328 * This function will fix the status of the localhost/all records in the mnet_host table
329 * checking they exist and adding them if missing + redefine CFG->mnet_localhost_id and
330 * CFG->mnet_all_hosts_id if needed + update all the users having non-existent mnethostid
331 * to correct CFG->mnet_localhost_id
332 *
333 * Implemented because, at some point, specially in old installations upgraded along
334 * multiple versions, sometimes the stuff above has ended being inconsistent, causing
c0002023 335 * problems here and there (noticeably in backup/restore). MDL-16879
02caf1e7 336 */
337function upgrade_fix_incorrect_mnethostids() {
338
339 global $CFG, $DB;
340
341/// Get current $CFG/mnet_host records
342 $old_mnet_localhost_id = !empty($CFG->mnet_localhost_id) ? $CFG->mnet_localhost_id : 0;
343 $old_mnet_all_hosts_id = !empty($CFG->mnet_all_hosts_id) ? $CFG->mnet_all_hosts_id : 0;
344
345 $current_mnet_localhost_host = $DB->get_record('mnet_host', array('wwwroot' => $CFG->wwwroot)); /// By wwwroot
346 $current_mnet_all_hosts_host = $DB->get_record_select('mnet_host', $DB->sql_isempty('mnet_host', 'wwwroot', false, false)); /// By empty wwwroot
347
7062a798
PL
348 if (!$moodleapplicationid = $DB->get_field('mnet_application', 'id', array('name' => 'moodle'))) {
349 $m = (object)array(
350 'name' => 'moodle',
351 'display_name' => 'Moodle',
352 'xmlrpc_server_url' => '/mnet/xmlrpc/server.php',
353 'sso_land_url' => '/auth/mnet/land.php',
c9606565 354 'sso_jump_url' => '/auth/mnet/jump.php',
7062a798
PL
355 );
356 $moodleapplicationid = $DB->insert_record('mnet_application', $m);
357 }
358
02caf1e7 359/// Create localhost_host if necessary (pretty improbable but better to be 100% in the safe side)
360/// Code stolen from mnet_environment->init
361 if (!$current_mnet_localhost_host) {
362 $current_mnet_localhost_host = new stdClass();
363 $current_mnet_localhost_host->wwwroot = $CFG->wwwroot;
364 $current_mnet_localhost_host->ip_address = '';
365 $current_mnet_localhost_host->public_key = '';
366 $current_mnet_localhost_host->public_key_expires = 0;
367 $current_mnet_localhost_host->last_connect_time = 0;
368 $current_mnet_localhost_host->last_log_id = 0;
369 $current_mnet_localhost_host->deleted = 0;
370 $current_mnet_localhost_host->name = '';
7062a798 371 $current_mnet_localhost_host->applicationid = $moodleapplicationid;
02caf1e7 372 /// Get the ip of the server
373 if (empty($_SERVER['SERVER_ADDR'])) {
374 /// SERVER_ADDR is only returned by Apache-like webservers
375 $count = preg_match("@^(?:http[s]?://)?([A-Z0-9\-\.]+).*@i", $current_mnet_localhost_host->wwwroot, $matches);
376 $my_hostname = $count > 0 ? $matches[1] : false;
377 $my_ip = gethostbyname($my_hostname); // Returns unmodified hostname on failure. DOH!
378 if ($my_ip == $my_hostname) {
379 $current_mnet_localhost_host->ip_address = 'UNKNOWN';
380 } else {
381 $current_mnet_localhost_host->ip_address = $my_ip;
382 }
383 } else {
384 $current_mnet_localhost_host->ip_address = $_SERVER['SERVER_ADDR'];
385 }
386 $current_mnet_localhost_host->id = $DB->insert_record('mnet_host', $current_mnet_localhost_host, true);
387 }
388
389/// Create all_hosts_host if necessary (pretty improbable but better to be 100% in the safe side)
390/// Code stolen from mnet_environment->init
391 if (!$current_mnet_all_hosts_host) {
392 $current_mnet_all_hosts_host = new stdClass();
393 $current_mnet_all_hosts_host->wwwroot = '';
394 $current_mnet_all_hosts_host->ip_address = '';
395 $current_mnet_all_hosts_host->public_key = '';
396 $current_mnet_all_hosts_host->public_key_expires = 0;
397 $current_mnet_all_hosts_host->last_connect_time = 0;
398 $current_mnet_all_hosts_host->last_log_id = 0;
399 $current_mnet_all_hosts_host->deleted = 0;
400 $current_mnet_all_hosts_host->name = 'All Hosts';
7062a798 401 $current_mnet_all_hosts_host->applicationid = $moodleapplicationid;
02caf1e7 402 $current_mnet_all_hosts_host->id = $DB->insert_record('mnet_host', $current_mnet_all_hosts_host, true);
403 }
404
405/// Compare old_mnet_localhost_id and current_mnet_localhost_host
406
407 if ($old_mnet_localhost_id != $current_mnet_localhost_host->id) { /// Different = problems
408 /// Update $CFG->mnet_localhost_id to correct value
409 set_config('mnet_localhost_id', $current_mnet_localhost_host->id);
410
411 /// Delete $old_mnet_localhost_id if exists (users will be assigned to new one below)
412 $DB->delete_records('mnet_host', array('id' => $old_mnet_localhost_id));
413 }
414
415/// Compare old_mnet_all_hosts_id and current_mnet_all_hosts_host
416
417 if ($old_mnet_all_hosts_id != $current_mnet_all_hosts_host->id) { /// Different = problems
418 /// Update $CFG->mnet_localhost_id to correct value
419 set_config('mnet_all_hosts_id', $current_mnet_all_hosts_host->id);
420
421 /// Delete $old_mnet_all_hosts_id if exists
422 $DB->delete_records('mnet_host', array('id' => $old_mnet_all_hosts_id));
423 }
424
425/// Finally, update all the incorrect user->mnethostid to the correct CFG->mnet_localhost_id, preventing UIX dupes
426 $hosts = $DB->get_records_menu('mnet_host', null, '', 'id, id AS id2');
427 list($in_sql, $in_params) = $DB->get_in_or_equal($hosts, SQL_PARAMS_QM, null, false);
428
429 $sql = "SELECT id
ba73136e 430 FROM {user} u1
02caf1e7 431 WHERE u1.mnethostid $in_sql
432 AND NOT EXISTS (
433 SELECT 'x'
ba73136e 434 FROM {user} u2
02caf1e7 435 WHERE u2.username = u1.username
436 AND u2.mnethostid = ?)";
437
438 $params = array_merge($in_params, array($current_mnet_localhost_host->id));
439
440 if ($rs = $DB->get_recordset_sql($sql, $params)) {
441 foreach ($rs as $rec) {
442 $DB->set_field('user', 'mnethostid', $current_mnet_localhost_host->id, array('id' => $rec->id));
443 upgrade_set_timeout(60); /// Give upgrade at least 60 more seconds
444 }
445 $rs->close();
446 }
7062a798
PL
447
448 // fix up any host records that have incorrect ids
449 $DB->set_field_select('mnet_host', 'applicationid', $moodleapplicationid, 'id = ? or id = ?', array($current_mnet_localhost_host->id, $current_mnet_all_hosts_host->id));
450
02caf1e7 451}
452
7d2a0492 453/**
454 * This function is used as part of the great navigation upgrade of 20090828
455 * It is used to clean up contexts that are unique to a blocks that are about
456 * to be removed.
457 *
458 *
459 * Look at {@link blocklib.php::blocks_delete_instance()} the function from
460 * which I based this code. It is important to mention one very important fact
461 * before doing this I checked that the blocks did not override the
462 * {@link block_base::instance_delete()} method. Should this function ever
463 * be repeated check this again
117bd748 464 *
7d2a0492 465 * @link lib/db/upgrade.php
466 *
467 * @since navigation upgrade 20090828
468 * @param array $contextidarray An array of block instance context ids
61676608 469 * @return void
7d2a0492 470 */
471function upgrade_cleanup_unwanted_block_contexts($contextidarray) {
472 global $DB;
473
474 if (!is_array($contextidarray) || count($contextidarray)===0) {
475 // Ummmm no instances?
61676608 476 return;
7d2a0492 477 }
478
479 $contextidstring = join(',', $contextidarray);
480
481 $blockcontexts = $DB->get_recordset_select('context', 'contextlevel = '.CONTEXT_BLOCK.' AND id IN ('.$contextidstring.')', array(), '', 'id, contextlevel');
482 $blockcontextids = array();
483 foreach ($blockcontexts as $blockcontext) {
484 $blockcontextids[] = $blockcontext->id;
485 }
486
487 if (count($blockcontextids)===0) {
488 // None of the instances have unique contexts
61676608 489 return;
7d2a0492 490 }
491
492 $blockcontextidsstring = join(',', $blockcontextids);
493
61676608
PS
494 $DB->delete_records_select('role_assignments', 'contextid IN ('.$blockcontextidsstring.')');
495 $DB->delete_records_select('role_capabilities', 'contextid IN ('.$blockcontextidsstring.')');
496 $DB->delete_records_select('role_names', 'contextid IN ('.$blockcontextidsstring.')');
497 $DB->delete_records_select('context', 'id IN ('.$blockcontextidsstring.')');
67e4713c 498}