Commit | Line | Data |
---|---|---|
bbd0e548 DW |
1 | <?php |
2 | // This file is part of Moodle - http://moodle.org/ | |
3 | // | |
4 | // Moodle is free software: you can redistribute it and/or modify | |
5 | // it under the terms of the GNU General Public License as published by | |
6 | // the Free Software Foundation, either version 3 of the License, or | |
7 | // (at your option) any later version. | |
8 | // | |
9 | // Moodle is distributed in the hope that it will be useful, | |
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | // GNU General Public License for more details. | |
13 | // | |
14 | // You should have received a copy of the GNU General Public License | |
15 | // along with Moodle. If not, see <http://www.gnu.org/licenses/>. | |
16 | ||
17 | /** | |
18 | * This file contains the upgrade code to upgrade from mod_assignment to mod_assign | |
19 | * | |
20 | * @package mod_assign | |
21 | * @copyright 2012 NetSpot {@link http://www.netspot.com.au} | |
22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
23 | */ | |
24 | ||
25 | defined('MOODLE_INTERNAL') || die(); | |
26 | ||
27 | /** Include locallib.php */ | |
28 | require_once($CFG->dirroot.'/mod/assign/locallib.php'); | |
29 | /** Include accesslib.php */ | |
30 | require_once($CFG->libdir.'/accesslib.php'); | |
31 | ||
5b121144 DW |
32 | /** |
33 | * The maximum amount of time to spend upgrading a single assignment. | |
34 | * This is intentionally generous (5 mins) as the effect of a timeout | |
35 | * for a legitimate upgrade would be quite harsh (roll back code will not run) | |
36 | */ | |
37 | define('ASSIGN_MAX_UPGRADE_TIME_SECS', 300); | |
bbd0e548 DW |
38 | |
39 | /** | |
40 | * Class to manage upgrades from mod_assignment to mod_assign | |
41 | * | |
42 | * @package mod_assign | |
43 | * @copyright 2012 NetSpot {@link http://www.netspot.com.au} | |
44 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
45 | */ | |
46 | class assign_upgrade_manager { | |
47 | ||
48 | /** | |
49 | * This function converts all of the base settings for an instance of | |
50 | * the old assignment to the new format. Then it calls each of the plugins | |
51 | * to see if they can help upgrade this assignment. | |
52 | * @param int $oldassignmentid (don't rely on the old assignment type even being installed) | |
53 | * @param string $log This string gets appended to during the conversion process | |
54 | * @return bool true or false | |
55 | */ | |
56 | public function upgrade_assignment($oldassignmentid, & $log) { | |
bbd0e548 | 57 | // steps to upgrade an assignment |
bbd0e548 DW |
58 | global $DB, $CFG, $USER; |
59 | // steps to upgrade an assignment | |
60 | ||
61 | // is the user the admin? admin check goes here | |
62 | if (!is_siteadmin($USER->id)) { | |
63 | return false; | |
64 | } | |
65 | ||
5b121144 DW |
66 | // should we use a shutdown handler to rollback on timeout? |
67 | @set_time_limit(ASSIGN_MAX_UPGRADE_TIME_SECS); | |
68 | ||
bbd0e548 DW |
69 | |
70 | // get the module details | |
71 | $oldmodule = $DB->get_record('modules', array('name'=>'assignment'), '*', MUST_EXIST); | |
72 | $oldcoursemodule = $DB->get_record('course_modules', array('module'=>$oldmodule->id, 'instance'=>$oldassignmentid), '*', MUST_EXIST); | |
73 | $oldcontext = context_module::instance($oldcoursemodule->id); | |
74 | ||
75 | // first insert an assign instance to get the id | |
76 | $oldassignment = $DB->get_record('assignment', array('id'=>$oldassignmentid), '*', MUST_EXIST); | |
77 | ||
78 | $oldversion = get_config('assignment_' . $oldassignment->assignmenttype, 'version'); | |
79 | ||
80 | $data = new stdClass(); | |
81 | $data->course = $oldassignment->course; | |
82 | $data->name = $oldassignment->name; | |
83 | $data->intro = $oldassignment->intro; | |
84 | $data->introformat = $oldassignment->introformat; | |
85 | $data->alwaysshowdescription = 1; | |
86 | $data->sendnotifications = $oldassignment->emailteachers; | |
56ff1300 | 87 | $data->sendlatenotifications = $oldassignment->emailteachers; |
bbd0e548 DW |
88 | $data->duedate = $oldassignment->timedue; |
89 | $data->allowsubmissionsfromdate = $oldassignment->timeavailable; | |
90 | $data->grade = $oldassignment->grade; | |
91 | $data->submissiondrafts = $oldassignment->resubmit; | |
94f26900 | 92 | $data->requiresubmissionstatement = 0; |
9e795179 DW |
93 | $data->cutoffdate = 0; |
94 | // New way to specify no late submissions. | |
95 | if ($oldassignment->preventlate) { | |
96 | $data->cutoffdate = $data->duedate; | |
97 | } | |
bbd0e548 DW |
98 | |
99 | $newassignment = new assign(null, null, null); | |
100 | ||
101 | if (!$newassignment->add_instance($data, false)) { | |
102 | $log = get_string('couldnotcreatenewassignmentinstance', 'mod_assign'); | |
103 | return false; | |
104 | } | |
105 | ||
106 | // now create a new coursemodule from the old one | |
107 | $newmodule = $DB->get_record('modules', array('name'=>'assign'), '*', MUST_EXIST); | |
108 | $newcoursemodule = $this->duplicate_course_module($oldcoursemodule, $newmodule->id, $newassignment->get_instance()->id); | |
109 | if (!$newcoursemodule) { | |
110 | $log = get_string('couldnotcreatenewcoursemodule', 'mod_assign'); | |
111 | return false; | |
112 | } | |
113 | ||
114 | // convert the base database tables (assignment, submission, grade) | |
115 | ||
116 | // these are used to store information in case a rollback is required | |
117 | $gradingarea = null; | |
118 | $gradingdefinitions = null; | |
119 | $gradeidmap = array(); | |
120 | $completiondone = false; | |
c45261df | 121 | $gradesdone = false; |
bbd0e548 DW |
122 | |
123 | // from this point we want to rollback on failure | |
124 | $rollback = false; | |
125 | try { | |
126 | $newassignment->set_context(context_module::instance($newcoursemodule->id)); | |
127 | ||
128 | // the course module has now been created - time to update the core tables | |
129 | ||
130 | // copy intro files | |
131 | $newassignment->copy_area_files_for_upgrade($oldcontext->id, 'mod_assignment', 'intro', 0, | |
132 | $newassignment->get_context()->id, 'mod_assign', 'intro', 0); | |
133 | ||
134 | ||
135 | // get the plugins to do their bit | |
136 | foreach ($newassignment->get_submission_plugins() as $plugin) { | |
137 | if ($plugin->can_upgrade($oldassignment->assignmenttype, $oldversion)) { | |
138 | $plugin->enable(); | |
139 | if (!$plugin->upgrade_settings($oldcontext, $oldassignment, $log)) { | |
140 | $rollback = true; | |
141 | } | |
142 | } | |
143 | } | |
144 | foreach ($newassignment->get_feedback_plugins() as $plugin) { | |
145 | if ($plugin->can_upgrade($oldassignment->assignmenttype, $oldversion)) { | |
146 | $plugin->enable(); | |
147 | if (!$plugin->upgrade_settings($oldcontext, $oldassignment, $log)) { | |
148 | $rollback = true; | |
149 | } | |
150 | } | |
151 | } | |
152 | ||
153 | // see if there is advanced grading upgrades required | |
154 | $gradingarea = $DB->get_record('grading_areas', array('contextid'=>$oldcontext->id, 'areaname'=>'submission'), '*', IGNORE_MISSING); | |
155 | if ($gradingarea) { | |
156 | $DB->update_record('grading_areas', array('id'=>$gradingarea->id, 'contextid'=>$newassignment->get_context()->id, 'component'=>'mod_assign', 'areaname'=>'submissions')); | |
157 | $gradingdefinitions = $DB->get_records('grading_definitions', array('areaid'=>$gradingarea->id)); | |
158 | } | |
159 | ||
160 | // upgrade completion data | |
161 | $DB->set_field('course_modules_completion', 'coursemoduleid', $newcoursemodule->id, array('coursemoduleid'=>$oldcoursemodule->id)); | |
1f417de8 DW |
162 | $allcriteria = $DB->get_records('course_completion_criteria', array('moduleinstance'=>$oldcoursemodule->id)); |
163 | foreach ($allcriteria as $criteria) { | |
164 | $criteria->module = 'assign'; | |
165 | $criteria->moduleinstance = $newcoursemodule->id; | |
166 | $DB->update_record('course_completion_criteria', $criteria); | |
167 | } | |
bbd0e548 DW |
168 | $completiondone = true; |
169 | ||
170 | ||
171 | // copy all the submission data (and get plugins to do their bit) | |
172 | $oldsubmissions = $DB->get_records('assignment_submissions', array('assignment'=>$oldassignmentid)); | |
173 | ||
174 | foreach ($oldsubmissions as $oldsubmission) { | |
175 | $submission = new stdClass(); | |
176 | $submission->assignment = $newassignment->get_instance()->id; | |
177 | $submission->userid = $oldsubmission->userid; | |
178 | $submission->timecreated = $oldsubmission->timecreated; | |
179 | $submission->timemodified = $oldsubmission->timemodified; | |
180 | $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED; | |
181 | $submission->id = $DB->insert_record('assign_submission', $submission); | |
182 | if (!$submission->id) { | |
183 | $log .= get_string('couldnotinsertsubmission', 'mod_assign', $submission->userid); | |
184 | $rollback = true; | |
185 | } | |
186 | foreach ($newassignment->get_submission_plugins() as $plugin) { | |
187 | if ($plugin->can_upgrade($oldassignment->assignmenttype, $oldversion)) { | |
188 | if (!$plugin->upgrade($oldcontext, $oldassignment, $oldsubmission, $submission, $log)) { | |
189 | $rollback = true; | |
190 | } | |
191 | } | |
192 | } | |
193 | if ($oldsubmission->timemarked) { | |
194 | // submission has been graded - create a grade record | |
195 | $grade = new stdClass(); | |
196 | $grade->assignment = $newassignment->get_instance()->id; | |
197 | $grade->userid = $oldsubmission->userid; | |
198 | $grade->grader = $oldsubmission->teacher; | |
199 | $grade->timemodified = $oldsubmission->timemarked; | |
200 | $grade->timecreated = $oldsubmission->timecreated; | |
201 | // $grade->locked = $oldsubmission->locked; | |
202 | $grade->grade = $oldsubmission->grade; | |
203 | $grade->mailed = $oldsubmission->mailed; | |
204 | $grade->id = $DB->insert_record('assign_grades', $grade); | |
205 | if (!$grade->id) { | |
206 | $log .= get_string('couldnotinsertgrade', 'mod_assign', $grade->userid); | |
207 | $rollback = true; | |
208 | } | |
209 | ||
210 | // copy any grading instances | |
211 | if ($gradingarea) { | |
212 | ||
213 | $gradeidmap[$grade->id] = $oldsubmission->id; | |
214 | ||
215 | foreach ($gradingdefinitions as $definition) { | |
216 | $DB->set_field('grading_instances', 'itemid', $grade->id, array('definitionid'=>$definition->id, 'itemid'=>$oldsubmission->id)); | |
217 | } | |
218 | ||
219 | } | |
220 | foreach ($newassignment->get_feedback_plugins() as $plugin) { | |
221 | if ($plugin->can_upgrade($oldassignment->assignmenttype, $oldversion)) { | |
222 | if (!$plugin->upgrade($oldcontext, $oldassignment, $oldsubmission, $grade, $log)) { | |
223 | $rollback = true; | |
224 | } | |
225 | } | |
226 | } | |
227 | } | |
228 | } | |
229 | ||
230 | $newassignment->update_calendar($newcoursemodule->id); | |
bbd0e548 DW |
231 | |
232 | // copy the grades from the old assignment to the new one | |
1f417de8 DW |
233 | |
234 | $gradeitem = $DB->get_record('grade_items', array('iteminstance'=>$oldassignment->id, 'itemmodule'=>'assignment'), 'id', IGNORE_MISSING); | |
235 | if ($gradeitem) { | |
236 | $gradeitem->iteminstance = $newassignment->get_instance()->id; | |
237 | $gradeitem->itemmodule = 'assign'; | |
238 | $DB->update_record('grade_items', $gradeitem); | |
239 | } | |
c45261df | 240 | $gradesdone = true; |
bbd0e548 DW |
241 | |
242 | } catch (Exception $exception) { | |
243 | $rollback = true; | |
5b121144 | 244 | $log .= get_string('conversionexception', 'mod_assign', $exception->error); |
bbd0e548 DW |
245 | } |
246 | ||
247 | if ($rollback) { | |
c45261df DW |
248 | // roll back the grades changes |
249 | if ($gradesdone) { | |
250 | // copy the grades from the old assignment to the new one | |
1f417de8 DW |
251 | $gradeitem = $DB->get_record('grade_items', array('iteminstance'=>$newassignment->get_instance()->id, 'itemmodule'=>'assign'), 'id', IGNORE_MISSING); |
252 | if ($gradeitem) { | |
253 | $gradeitem->iteminstance = $oldassignment->id; | |
254 | $gradeitem->itemmodule = 'assignment'; | |
255 | $DB->update_record('grade_items', $gradeitem); | |
256 | } | |
c45261df | 257 | } |
bbd0e548 DW |
258 | // roll back the completion changes |
259 | if ($completiondone) { | |
260 | $DB->set_field('course_modules_completion', 'coursemoduleid', $oldcoursemodule->id, array('coursemoduleid'=>$newcoursemodule->id)); | |
1f417de8 DW |
261 | $allcriteria = $DB->get_records('course_completion_criteria', array('moduleinstance'=>$newcoursemodule->id)); |
262 | foreach ($allcriteria as $criteria) { | |
263 | $criteria->module = 'assignment'; | |
264 | $criteria->moduleinstance = $oldcoursemodule->id; | |
265 | $DB->update_record('course_completion_criteria', $criteria); | |
266 | } | |
bbd0e548 DW |
267 | } |
268 | // roll back the advanced grading update | |
269 | if ($gradingarea) { | |
270 | foreach ($gradeidmap as $newgradeid => $oldsubmissionid) { | |
271 | foreach ($gradingdefinitions as $definition) { | |
272 | $DB->set_field('grading_instances', 'itemid', $oldsubmissionid, array('definitionid'=>$definition->id, 'itemid'=>$newgradeid)); | |
273 | } | |
274 | } | |
275 | $DB->update_record('grading_areas', array('id'=>$gradingarea->id, 'contextid'=>$oldcontext->id, 'component'=>'mod_assignment', 'areaname'=>'submission')); | |
276 | } | |
277 | $newassignment->delete_instance(); | |
278 | ||
279 | return false; | |
280 | } | |
281 | // all is well, | |
282 | // delete the old assignment (use object delete) | |
283 | $cm = get_coursemodule_from_id('', $oldcoursemodule->id, $oldcoursemodule->course); | |
284 | if ($cm) { | |
285 | $this->delete_course_module($cm); | |
286 | } | |
287 | rebuild_course_cache($oldcoursemodule->course); | |
288 | return true; | |
289 | } | |
290 | ||
291 | ||
292 | /** | |
293 | * Create a duplicate course module record so we can create the upgraded | |
294 | * assign module alongside the old assignment module. | |
295 | * | |
296 | * @param stdClass $cm The old course module record | |
297 | * @param int $moduleid The id of the new assign module | |
298 | * @param int $newinstanceid The id of the new instance of the assign module | |
299 | * @return mixed stdClass|bool The new course module record or FALSE | |
300 | */ | |
301 | private function duplicate_course_module(stdClass $cm, $moduleid, $newinstanceid) { | |
302 | global $DB, $CFG; | |
303 | ||
304 | $newcm = new stdClass(); | |
305 | $newcm->course = $cm->course; | |
306 | $newcm->module = $moduleid; | |
307 | $newcm->instance = $newinstanceid; | |
308 | $newcm->visible = $cm->visible; | |
309 | $newcm->section = $cm->section; | |
310 | $newcm->score = $cm->score; | |
311 | $newcm->indent = $cm->indent; | |
312 | $newcm->groupmode = $cm->groupmode; | |
313 | $newcm->groupingid = $cm->groupingid; | |
314 | $newcm->groupmembersonly = $cm->groupmembersonly; | |
315 | $newcm->completion = $cm->completion; | |
316 | $newcm->completiongradeitemnumber = $cm->completiongradeitemnumber; | |
317 | $newcm->completionview = $cm->completionview; | |
318 | $newcm->completionexpected = $cm->completionexpected; | |
319 | if(!empty($CFG->enableavailability)) { | |
320 | $newcm->availablefrom = $cm->availablefrom; | |
321 | $newcm->availableuntil = $cm->availableuntil; | |
322 | $newcm->showavailability = $cm->showavailability; | |
323 | } | |
324 | $newcm->showdescription = $cm->showdescription; | |
325 | ||
326 | $newcmid = add_course_module($newcm); | |
327 | $newcm = get_coursemodule_from_id('', $newcmid, $cm->course); | |
328 | if (!$newcm) { | |
329 | return false; | |
330 | } | |
331 | $section = $DB->get_record("course_sections", array("id"=>$newcm->section)); | |
332 | if (!$section) { | |
333 | return false; | |
334 | } | |
335 | ||
336 | $mod = new stdClass(); | |
337 | $mod->course = $newcm->course; | |
338 | $mod->section = $section->section; | |
339 | $mod->coursemodule = $newcm->id; | |
340 | $mod->id = $newcm->id; | |
341 | $newcm->section = add_mod_to_section($mod, $cm); | |
342 | ||
343 | // make sure visibility is set correctly (in particular in calendar) | |
344 | // note: allow them to set it even without moodle/course:activityvisibility | |
345 | set_coursemodule_visible($newcm->id, $newcm->visible); | |
346 | ||
347 | return $newcm; | |
348 | } | |
349 | ||
350 | /** | |
351 | * This function deletes the old assignment course module after | |
352 | * it has been upgraded. This code is adapted from "course/mod.php". | |
353 | * | |
354 | * @param stdClass $cm The course module to delete. | |
355 | * @return bool | |
356 | */ | |
357 | private function delete_course_module($cm) { | |
358 | global $CFG, $USER, $DB, $OUTPUT; | |
359 | $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST); | |
360 | ||
361 | $coursecontext = context_course::instance($course->id); | |
362 | $modcontext = context_module::instance($cm->id); | |
363 | ||
364 | $modlib = "$CFG->dirroot/mod/$cm->modname/lib.php"; | |
365 | ||
366 | if (file_exists($modlib)) { | |
367 | require_once($modlib); | |
368 | } else { | |
369 | print_error('modulemissingcode', '', '', $modlib); | |
370 | } | |
371 | ||
372 | $deleteinstancefunction = $cm->modname."_delete_instance"; | |
373 | ||
374 | if (!$deleteinstancefunction($cm->instance)) { | |
375 | echo $OUTPUT->notification("Could not delete the $cm->modname (instance)"); | |
376 | } | |
377 | ||
378 | // remove all module files in case modules forget to do that | |
379 | $fs = get_file_storage(); | |
380 | $fs->delete_area_files($modcontext->id); | |
381 | ||
382 | if (!delete_course_module($cm->id)) { | |
383 | echo $OUTPUT->notification("Could not delete the $cm->modname (coursemodule)"); | |
384 | } | |
385 | if (!delete_mod_from_section($cm->id, $cm->section)) { | |
386 | echo $OUTPUT->notification("Could not delete the $cm->modname from that section"); | |
387 | } | |
388 | ||
389 | // Trigger a mod_deleted event with information about this module. | |
390 | $eventdata = new stdClass(); | |
391 | $eventdata->modulename = $cm->modname; | |
392 | $eventdata->cmid = $cm->id; | |
393 | $eventdata->courseid = $course->id; | |
394 | $eventdata->userid = $USER->id; | |
395 | events_trigger('mod_deleted', $eventdata); | |
396 | ||
397 | add_to_log($course->id, 'course', "delete mod", | |
398 | "view.php?id=$cm->course", | |
399 | "$cm->modname $cm->instance", $cm->id); | |
400 | ||
401 | return true; | |
402 | } | |
403 | ||
bbd0e548 | 404 | } |