42ff9ce6 |
1 | <?php //$Id$ |
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 |
10 | function 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 | */ |
61 | function 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 | */ |
85 | function 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 | */ |
170 | function 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 | */ |
235 | function 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 | |
246 | /// Create localhost_host if necessary (pretty improbable but better to be 100% in the safe side) |
247 | /// Code stolen from mnet_environment->init |
248 | if (!$current_mnet_localhost_host) { |
249 | $current_mnet_localhost_host = new stdClass(); |
250 | $current_mnet_localhost_host->wwwroot = $CFG->wwwroot; |
251 | $current_mnet_localhost_host->ip_address = ''; |
252 | $current_mnet_localhost_host->public_key = ''; |
253 | $current_mnet_localhost_host->public_key_expires = 0; |
254 | $current_mnet_localhost_host->last_connect_time = 0; |
255 | $current_mnet_localhost_host->last_log_id = 0; |
256 | $current_mnet_localhost_host->deleted = 0; |
257 | $current_mnet_localhost_host->name = ''; |
258 | /// Get the ip of the server |
259 | if (empty($_SERVER['SERVER_ADDR'])) { |
260 | /// SERVER_ADDR is only returned by Apache-like webservers |
261 | $count = preg_match("@^(?:http[s]?://)?([A-Z0-9\-\.]+).*@i", $current_mnet_localhost_host->wwwroot, $matches); |
262 | $my_hostname = $count > 0 ? $matches[1] : false; |
263 | $my_ip = gethostbyname($my_hostname); // Returns unmodified hostname on failure. DOH! |
264 | if ($my_ip == $my_hostname) { |
265 | $current_mnet_localhost_host->ip_address = 'UNKNOWN'; |
266 | } else { |
267 | $current_mnet_localhost_host->ip_address = $my_ip; |
268 | } |
269 | } else { |
270 | $current_mnet_localhost_host->ip_address = $_SERVER['SERVER_ADDR']; |
271 | } |
272 | $current_mnet_localhost_host->id = $DB->insert_record('mnet_host', $current_mnet_localhost_host, true); |
273 | } |
274 | |
275 | /// Create all_hosts_host if necessary (pretty improbable but better to be 100% in the safe side) |
276 | /// Code stolen from mnet_environment->init |
277 | if (!$current_mnet_all_hosts_host) { |
278 | $current_mnet_all_hosts_host = new stdClass(); |
279 | $current_mnet_all_hosts_host->wwwroot = ''; |
280 | $current_mnet_all_hosts_host->ip_address = ''; |
281 | $current_mnet_all_hosts_host->public_key = ''; |
282 | $current_mnet_all_hosts_host->public_key_expires = 0; |
283 | $current_mnet_all_hosts_host->last_connect_time = 0; |
284 | $current_mnet_all_hosts_host->last_log_id = 0; |
285 | $current_mnet_all_hosts_host->deleted = 0; |
286 | $current_mnet_all_hosts_host->name = 'All Hosts'; |
287 | $current_mnet_all_hosts_host->id = $DB->insert_record('mnet_host', $current_mnet_all_hosts_host, true); |
288 | } |
289 | |
290 | /// Compare old_mnet_localhost_id and current_mnet_localhost_host |
291 | |
292 | if ($old_mnet_localhost_id != $current_mnet_localhost_host->id) { /// Different = problems |
293 | /// Update $CFG->mnet_localhost_id to correct value |
294 | set_config('mnet_localhost_id', $current_mnet_localhost_host->id); |
295 | |
296 | /// Delete $old_mnet_localhost_id if exists (users will be assigned to new one below) |
297 | $DB->delete_records('mnet_host', array('id' => $old_mnet_localhost_id)); |
298 | } |
299 | |
300 | /// Compare old_mnet_all_hosts_id and current_mnet_all_hosts_host |
301 | |
302 | if ($old_mnet_all_hosts_id != $current_mnet_all_hosts_host->id) { /// Different = problems |
303 | /// Update $CFG->mnet_localhost_id to correct value |
304 | set_config('mnet_all_hosts_id', $current_mnet_all_hosts_host->id); |
305 | |
306 | /// Delete $old_mnet_all_hosts_id if exists |
307 | $DB->delete_records('mnet_host', array('id' => $old_mnet_all_hosts_id)); |
308 | } |
309 | |
310 | /// Finally, update all the incorrect user->mnethostid to the correct CFG->mnet_localhost_id, preventing UIX dupes |
311 | $hosts = $DB->get_records_menu('mnet_host', null, '', 'id, id AS id2'); |
312 | list($in_sql, $in_params) = $DB->get_in_or_equal($hosts, SQL_PARAMS_QM, null, false); |
313 | |
314 | $sql = "SELECT id |
ba73136e |
315 | FROM {user} u1 |
02caf1e7 |
316 | WHERE u1.mnethostid $in_sql |
317 | AND NOT EXISTS ( |
318 | SELECT 'x' |
ba73136e |
319 | FROM {user} u2 |
02caf1e7 |
320 | WHERE u2.username = u1.username |
321 | AND u2.mnethostid = ?)"; |
322 | |
323 | $params = array_merge($in_params, array($current_mnet_localhost_host->id)); |
324 | |
325 | if ($rs = $DB->get_recordset_sql($sql, $params)) { |
326 | foreach ($rs as $rec) { |
327 | $DB->set_field('user', 'mnethostid', $current_mnet_localhost_host->id, array('id' => $rec->id)); |
328 | upgrade_set_timeout(60); /// Give upgrade at least 60 more seconds |
329 | } |
330 | $rs->close(); |
331 | } |
332 | } |
333 | |
7d2a0492 |
334 | /** |
335 | * This function is used as part of the great navigation upgrade of 20090828 |
336 | * It is used to clean up contexts that are unique to a blocks that are about |
337 | * to be removed. |
338 | * |
339 | * |
340 | * Look at {@link blocklib.php::blocks_delete_instance()} the function from |
341 | * which I based this code. It is important to mention one very important fact |
342 | * before doing this I checked that the blocks did not override the |
343 | * {@link block_base::instance_delete()} method. Should this function ever |
344 | * be repeated check this again |
345 | * |
346 | * @link lib/db/upgrade.php |
347 | * |
348 | * @since navigation upgrade 20090828 |
349 | * @param array $contextidarray An array of block instance context ids |
350 | * @return bool |
351 | */ |
352 | function upgrade_cleanup_unwanted_block_contexts($contextidarray) { |
353 | global $DB; |
354 | |
355 | if (!is_array($contextidarray) || count($contextidarray)===0) { |
356 | // Ummmm no instances? |
357 | return true; |
358 | } |
359 | |
360 | $contextidstring = join(',', $contextidarray); |
361 | |
362 | $blockcontexts = $DB->get_recordset_select('context', 'contextlevel = '.CONTEXT_BLOCK.' AND id IN ('.$contextidstring.')', array(), '', 'id, contextlevel'); |
363 | $blockcontextids = array(); |
364 | foreach ($blockcontexts as $blockcontext) { |
365 | $blockcontextids[] = $blockcontext->id; |
366 | } |
367 | |
368 | if (count($blockcontextids)===0) { |
369 | // None of the instances have unique contexts |
370 | return true; |
371 | } |
372 | |
373 | $blockcontextidsstring = join(',', $blockcontextids); |
374 | |
375 | $outcome1 = $DB->delete_records_select('role_assignments', 'contextid IN ('.$blockcontextidsstring.')'); |
376 | $outcome2 = $DB->delete_records_select('role_capabilities', 'contextid IN ('.$blockcontextidsstring.')'); |
377 | $outcome3 = $DB->delete_records_select('role_names', 'contextid IN ('.$blockcontextidsstring.')'); |
378 | $outcome4 = $DB->delete_records_select('context', 'id IN ('.$blockcontextidsstring.')'); |
379 | |
380 | return ($outcome1 && $outcome2 && $outcome4 && $outcome4); |
381 | } |
382 | |
02caf1e7 |
383 | ?> |