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 | ||
bbd0e548 | 27 | require_once($CFG->dirroot.'/mod/assign/locallib.php'); |
bbd0e548 | 28 | require_once($CFG->libdir.'/accesslib.php'); |
a347aee3 | 29 | require_once($CFG->dirroot.'/course/lib.php'); |
bbd0e548 | 30 | |
e5403f8c | 31 | /* |
5b121144 DW |
32 | * The maximum amount of time to spend upgrading a single assignment. |
33 | * This is intentionally generous (5 mins) as the effect of a timeout | |
34 | * for a legitimate upgrade would be quite harsh (roll back code will not run) | |
35 | */ | |
36 | define('ASSIGN_MAX_UPGRADE_TIME_SECS', 300); | |
bbd0e548 DW |
37 | |
38 | /** | |
39 | * Class to manage upgrades from mod_assignment to mod_assign | |
40 | * | |
41 | * @package mod_assign | |
42 | * @copyright 2012 NetSpot {@link http://www.netspot.com.au} | |
43 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
44 | */ | |
45 | class assign_upgrade_manager { | |
46 | ||
47 | /** | |
48 | * This function converts all of the base settings for an instance of | |
49 | * the old assignment to the new format. Then it calls each of the plugins | |
50 | * to see if they can help upgrade this assignment. | |
51 | * @param int $oldassignmentid (don't rely on the old assignment type even being installed) | |
52 | * @param string $log This string gets appended to during the conversion process | |
53 | * @return bool true or false | |
54 | */ | |
55 | public function upgrade_assignment($oldassignmentid, & $log) { | |
bbd0e548 | 56 | global $DB, $CFG, $USER; |
e5403f8c | 57 | // Steps to upgrade an assignment. |
bbd0e548 | 58 | |
e5403f8c | 59 | // Is the user the admin? admin check goes here. |
bbd0e548 DW |
60 | if (!is_siteadmin($USER->id)) { |
61 | return false; | |
62 | } | |
63 | ||
5b121144 DW |
64 | @set_time_limit(ASSIGN_MAX_UPGRADE_TIME_SECS); |
65 | ||
e5403f8c | 66 | // Get the module details. |
bbd0e548 | 67 | $oldmodule = $DB->get_record('modules', array('name'=>'assignment'), '*', MUST_EXIST); |
e5403f8c DW |
68 | $params = array('module'=>$oldmodule->id, 'instance'=>$oldassignmentid); |
69 | $oldcoursemodule = $DB->get_record('course_modules', | |
70 | $params, | |
71 | '*', | |
72 | MUST_EXIST); | |
bbd0e548 DW |
73 | $oldcontext = context_module::instance($oldcoursemodule->id); |
74 | ||
e5403f8c | 75 | // First insert an assign instance to get the id. |
bbd0e548 DW |
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 | } | |
7c7bc2ee DW |
98 | $data->teamsubmission = 0; |
99 | $data->requireallteammemberssubmit = 0; | |
100 | $data->teamsubmissiongroupingid = 0; | |
101 | $data->blindmarking = 0; | |
bbd0e548 DW |
102 | |
103 | $newassignment = new assign(null, null, null); | |
104 | ||
105 | if (!$newassignment->add_instance($data, false)) { | |
106 | $log = get_string('couldnotcreatenewassignmentinstance', 'mod_assign'); | |
107 | return false; | |
108 | } | |
109 | ||
e5403f8c | 110 | // Now create a new coursemodule from the old one. |
bbd0e548 | 111 | $newmodule = $DB->get_record('modules', array('name'=>'assign'), '*', MUST_EXIST); |
e5403f8c DW |
112 | $newcoursemodule = $this->duplicate_course_module($oldcoursemodule, |
113 | $newmodule->id, | |
114 | $newassignment->get_instance()->id); | |
bbd0e548 DW |
115 | if (!$newcoursemodule) { |
116 | $log = get_string('couldnotcreatenewcoursemodule', 'mod_assign'); | |
117 | return false; | |
118 | } | |
119 | ||
e5403f8c | 120 | // Convert the base database tables (assignment, submission, grade). |
bbd0e548 | 121 | |
e5403f8c | 122 | // These are used to store information in case a rollback is required. |
bbd0e548 DW |
123 | $gradingarea = null; |
124 | $gradingdefinitions = null; | |
125 | $gradeidmap = array(); | |
126 | $completiondone = false; | |
c45261df | 127 | $gradesdone = false; |
bbd0e548 | 128 | |
e5403f8c | 129 | // From this point we want to rollback on failure. |
bbd0e548 DW |
130 | $rollback = false; |
131 | try { | |
132 | $newassignment->set_context(context_module::instance($newcoursemodule->id)); | |
133 | ||
e5403f8c | 134 | // The course module has now been created - time to update the core tables. |
bbd0e548 | 135 | |
e5403f8c | 136 | // Copy intro files. |
bbd0e548 DW |
137 | $newassignment->copy_area_files_for_upgrade($oldcontext->id, 'mod_assignment', 'intro', 0, |
138 | $newassignment->get_context()->id, 'mod_assign', 'intro', 0); | |
139 | ||
e5403f8c | 140 | // Get the plugins to do their bit. |
bbd0e548 DW |
141 | foreach ($newassignment->get_submission_plugins() as $plugin) { |
142 | if ($plugin->can_upgrade($oldassignment->assignmenttype, $oldversion)) { | |
143 | $plugin->enable(); | |
144 | if (!$plugin->upgrade_settings($oldcontext, $oldassignment, $log)) { | |
145 | $rollback = true; | |
146 | } | |
fb11a702 LT |
147 | } else { |
148 | $plugin->disable(); | |
bbd0e548 DW |
149 | } |
150 | } | |
151 | foreach ($newassignment->get_feedback_plugins() as $plugin) { | |
152 | if ($plugin->can_upgrade($oldassignment->assignmenttype, $oldversion)) { | |
153 | $plugin->enable(); | |
154 | if (!$plugin->upgrade_settings($oldcontext, $oldassignment, $log)) { | |
155 | $rollback = true; | |
156 | } | |
fb11a702 LT |
157 | } else { |
158 | $plugin->disable(); | |
bbd0e548 DW |
159 | } |
160 | } | |
161 | ||
e5403f8c DW |
162 | // See if there is advanced grading upgrades required. |
163 | $gradingarea = $DB->get_record('grading_areas', | |
164 | array('contextid'=>$oldcontext->id, 'areaname'=>'submission'), | |
165 | '*', | |
166 | IGNORE_MISSING); | |
bbd0e548 | 167 | if ($gradingarea) { |
e5403f8c DW |
168 | $params = array('id'=>$gradingarea->id, |
169 | 'contextid'=>$newassignment->get_context()->id, | |
170 | 'component'=>'mod_assign', | |
171 | 'areaname'=>'submissions'); | |
172 | $DB->update_record('grading_areas', $params); | |
173 | $gradingdefinitions = $DB->get_records('grading_definitions', | |
174 | array('areaid'=>$gradingarea->id)); | |
bbd0e548 DW |
175 | } |
176 | ||
7720f485 DW |
177 | // Upgrade availability data. |
178 | $DB->set_field('course_modules_avail_fields', | |
179 | 'coursemoduleid', | |
180 | $newcoursemodule->id, | |
181 | array('coursemoduleid'=>$oldcoursemodule->id)); | |
182 | $DB->set_field('course_modules_availability', | |
183 | 'coursemoduleid', | |
184 | $newcoursemodule->id, | |
185 | array('coursemoduleid'=>$oldcoursemodule->id)); | |
186 | $DB->set_field('course_modules_availability', | |
187 | 'sourcecmid', | |
188 | $newcoursemodule->id, | |
189 | array('sourcecmid'=>$oldcoursemodule->id)); | |
190 | $DB->set_field('course_sections_availability', | |
191 | 'sourcecmid', | |
192 | $newcoursemodule->id, | |
193 | array('sourcecmid'=>$oldcoursemodule->id)); | |
194 | ||
e5403f8c DW |
195 | // Upgrade completion data. |
196 | $DB->set_field('course_modules_completion', | |
197 | 'coursemoduleid', | |
198 | $newcoursemodule->id, | |
199 | array('coursemoduleid'=>$oldcoursemodule->id)); | |
200 | $allcriteria = $DB->get_records('course_completion_criteria', | |
201 | array('moduleinstance'=>$oldcoursemodule->id)); | |
1f417de8 DW |
202 | foreach ($allcriteria as $criteria) { |
203 | $criteria->module = 'assign'; | |
204 | $criteria->moduleinstance = $newcoursemodule->id; | |
205 | $DB->update_record('course_completion_criteria', $criteria); | |
206 | } | |
bbd0e548 DW |
207 | $completiondone = true; |
208 | ||
ac9ac1fe AO |
209 | // Migrate log entries so we don't lose them. |
210 | $logparams = array('cmid' => $oldcoursemodule->id, 'course' => $oldcoursemodule->course); | |
211 | $DB->set_field('log', 'module', 'assign', $logparams); | |
212 | $DB->set_field('log', 'cmid', $newcoursemodule->id, $logparams); | |
213 | ||
e5403f8c DW |
214 | // Copy all the submission data (and get plugins to do their bit). |
215 | $oldsubmissions = $DB->get_records('assignment_submissions', | |
216 | array('assignment'=>$oldassignmentid)); | |
bbd0e548 DW |
217 | |
218 | foreach ($oldsubmissions as $oldsubmission) { | |
219 | $submission = new stdClass(); | |
220 | $submission->assignment = $newassignment->get_instance()->id; | |
221 | $submission->userid = $oldsubmission->userid; | |
222 | $submission->timecreated = $oldsubmission->timecreated; | |
223 | $submission->timemodified = $oldsubmission->timemodified; | |
224 | $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED; | |
225 | $submission->id = $DB->insert_record('assign_submission', $submission); | |
226 | if (!$submission->id) { | |
227 | $log .= get_string('couldnotinsertsubmission', 'mod_assign', $submission->userid); | |
228 | $rollback = true; | |
229 | } | |
230 | foreach ($newassignment->get_submission_plugins() as $plugin) { | |
231 | if ($plugin->can_upgrade($oldassignment->assignmenttype, $oldversion)) { | |
e5403f8c DW |
232 | if (!$plugin->upgrade($oldcontext, |
233 | $oldassignment, | |
234 | $oldsubmission, | |
235 | $submission, | |
236 | $log)) { | |
bbd0e548 DW |
237 | $rollback = true; |
238 | } | |
239 | } | |
240 | } | |
241 | if ($oldsubmission->timemarked) { | |
e5403f8c | 242 | // Submission has been graded - create a grade record. |
bbd0e548 DW |
243 | $grade = new stdClass(); |
244 | $grade->assignment = $newassignment->get_instance()->id; | |
245 | $grade->userid = $oldsubmission->userid; | |
246 | $grade->grader = $oldsubmission->teacher; | |
247 | $grade->timemodified = $oldsubmission->timemarked; | |
248 | $grade->timecreated = $oldsubmission->timecreated; | |
bbd0e548 DW |
249 | $grade->grade = $oldsubmission->grade; |
250 | $grade->mailed = $oldsubmission->mailed; | |
251 | $grade->id = $DB->insert_record('assign_grades', $grade); | |
252 | if (!$grade->id) { | |
253 | $log .= get_string('couldnotinsertgrade', 'mod_assign', $grade->userid); | |
254 | $rollback = true; | |
255 | } | |
256 | ||
e5403f8c | 257 | // Copy any grading instances. |
bbd0e548 DW |
258 | if ($gradingarea) { |
259 | ||
260 | $gradeidmap[$grade->id] = $oldsubmission->id; | |
261 | ||
262 | foreach ($gradingdefinitions as $definition) { | |
e5403f8c DW |
263 | $params = array('definitionid'=>$definition->id, |
264 | 'itemid'=>$oldsubmission->id); | |
265 | $DB->set_field('grading_instances', 'itemid', $grade->id, $params); | |
bbd0e548 DW |
266 | } |
267 | ||
268 | } | |
269 | foreach ($newassignment->get_feedback_plugins() as $plugin) { | |
270 | if ($plugin->can_upgrade($oldassignment->assignmenttype, $oldversion)) { | |
e5403f8c DW |
271 | if (!$plugin->upgrade($oldcontext, |
272 | $oldassignment, | |
273 | $oldsubmission, | |
274 | $grade, | |
275 | $log)) { | |
bbd0e548 DW |
276 | $rollback = true; |
277 | } | |
278 | } | |
279 | } | |
280 | } | |
281 | } | |
282 | ||
283 | $newassignment->update_calendar($newcoursemodule->id); | |
bbd0e548 | 284 | |
f264499f DW |
285 | // Reassociate grade_items from the old assignment instance to the new assign instance. |
286 | // This includes outcome linked grade_items. | |
287 | $params = array('assign', $newassignment->get_instance()->id, 'assignment', $oldassignment->id); | |
288 | $sql = 'UPDATE {grade_items} SET itemmodule = ?, iteminstance = ? WHERE itemmodule = ? AND iteminstance = ?'; | |
289 | $DB->execute($sql, $params); | |
1f417de8 | 290 | |
c45261df | 291 | $gradesdone = true; |
bbd0e548 DW |
292 | |
293 | } catch (Exception $exception) { | |
294 | $rollback = true; | |
5b121144 | 295 | $log .= get_string('conversionexception', 'mod_assign', $exception->error); |
bbd0e548 DW |
296 | } |
297 | ||
298 | if ($rollback) { | |
e5403f8c | 299 | // Roll back the grades changes. |
c45261df | 300 | if ($gradesdone) { |
f264499f DW |
301 | // Reassociate grade_items from the new assign instance to the old assignment instance. |
302 | $params = array('assignment', $oldassignment->id, 'assign', $newassignment->get_instance()->id); | |
303 | $sql = 'UPDATE {grade_items} SET itemmodule = ?, iteminstance = ? WHERE itemmodule = ? AND iteminstance = ?'; | |
304 | $DB->execute($sql, $params); | |
c45261df | 305 | } |
e5403f8c | 306 | // Roll back the completion changes. |
bbd0e548 | 307 | if ($completiondone) { |
e5403f8c DW |
308 | $DB->set_field('course_modules_completion', |
309 | 'coursemoduleid', | |
310 | $oldcoursemodule->id, | |
311 | array('coursemoduleid'=>$newcoursemodule->id)); | |
312 | ||
313 | $allcriteria = $DB->get_records('course_completion_criteria', | |
314 | array('moduleinstance'=>$newcoursemodule->id)); | |
1f417de8 DW |
315 | foreach ($allcriteria as $criteria) { |
316 | $criteria->module = 'assignment'; | |
317 | $criteria->moduleinstance = $oldcoursemodule->id; | |
318 | $DB->update_record('course_completion_criteria', $criteria); | |
319 | } | |
bbd0e548 | 320 | } |
e5403f8c | 321 | // Roll back the log changes. |
ac9ac1fe AO |
322 | $logparams = array('cmid' => $newcoursemodule->id, 'course' => $newcoursemodule->course); |
323 | $DB->set_field('log', 'module', 'assignment', $logparams); | |
324 | $DB->set_field('log', 'cmid', $oldcoursemodule->id, $logparams); | |
e5403f8c | 325 | // Roll back the advanced grading update. |
bbd0e548 DW |
326 | if ($gradingarea) { |
327 | foreach ($gradeidmap as $newgradeid => $oldsubmissionid) { | |
328 | foreach ($gradingdefinitions as $definition) { | |
e5403f8c DW |
329 | $DB->set_field('grading_instances', |
330 | 'itemid', | |
331 | $oldsubmissionid, | |
332 | array('definitionid'=>$definition->id, 'itemid'=>$newgradeid)); | |
bbd0e548 DW |
333 | } |
334 | } | |
e5403f8c DW |
335 | $params = array('id'=>$gradingarea->id, |
336 | 'contextid'=>$oldcontext->id, | |
337 | 'component'=>'mod_assignment', | |
338 | 'areaname'=>'submission'); | |
339 | $DB->update_record('grading_areas', $params); | |
bbd0e548 DW |
340 | } |
341 | $newassignment->delete_instance(); | |
342 | ||
343 | return false; | |
344 | } | |
e5403f8c | 345 | // Delete the old assignment (use object delete). |
bbd0e548 DW |
346 | $cm = get_coursemodule_from_id('', $oldcoursemodule->id, $oldcoursemodule->course); |
347 | if ($cm) { | |
a347aee3 | 348 | course_delete_module($cm->id); |
bbd0e548 DW |
349 | } |
350 | rebuild_course_cache($oldcoursemodule->course); | |
351 | return true; | |
352 | } | |
353 | ||
354 | ||
355 | /** | |
356 | * Create a duplicate course module record so we can create the upgraded | |
357 | * assign module alongside the old assignment module. | |
358 | * | |
359 | * @param stdClass $cm The old course module record | |
360 | * @param int $moduleid The id of the new assign module | |
361 | * @param int $newinstanceid The id of the new instance of the assign module | |
362 | * @return mixed stdClass|bool The new course module record or FALSE | |
363 | */ | |
364 | private function duplicate_course_module(stdClass $cm, $moduleid, $newinstanceid) { | |
365 | global $DB, $CFG; | |
366 | ||
367 | $newcm = new stdClass(); | |
368 | $newcm->course = $cm->course; | |
369 | $newcm->module = $moduleid; | |
370 | $newcm->instance = $newinstanceid; | |
371 | $newcm->visible = $cm->visible; | |
372 | $newcm->section = $cm->section; | |
373 | $newcm->score = $cm->score; | |
374 | $newcm->indent = $cm->indent; | |
375 | $newcm->groupmode = $cm->groupmode; | |
376 | $newcm->groupingid = $cm->groupingid; | |
377 | $newcm->groupmembersonly = $cm->groupmembersonly; | |
378 | $newcm->completion = $cm->completion; | |
379 | $newcm->completiongradeitemnumber = $cm->completiongradeitemnumber; | |
380 | $newcm->completionview = $cm->completionview; | |
381 | $newcm->completionexpected = $cm->completionexpected; | |
e5403f8c | 382 | if (!empty($CFG->enableavailability)) { |
bbd0e548 DW |
383 | $newcm->availablefrom = $cm->availablefrom; |
384 | $newcm->availableuntil = $cm->availableuntil; | |
385 | $newcm->showavailability = $cm->showavailability; | |
386 | } | |
387 | $newcm->showdescription = $cm->showdescription; | |
388 | ||
389 | $newcmid = add_course_module($newcm); | |
390 | $newcm = get_coursemodule_from_id('', $newcmid, $cm->course); | |
391 | if (!$newcm) { | |
392 | return false; | |
393 | } | |
394 | $section = $DB->get_record("course_sections", array("id"=>$newcm->section)); | |
395 | if (!$section) { | |
396 | return false; | |
397 | } | |
398 | ||
bdc4cb9e | 399 | $newcm->section = course_add_cm_to_section($newcm->course, $newcm->id, $section->section, $cm->id); |
bbd0e548 | 400 | |
e5403f8c DW |
401 | // Make sure visibility is set correctly (in particular in calendar). |
402 | // Note: Allow them to set it even without moodle/course:activityvisibility. | |
bbd0e548 DW |
403 | set_coursemodule_visible($newcm->id, $newcm->visible); |
404 | ||
405 | return $newcm; | |
406 | } | |
bbd0e548 | 407 | } |