Commit | Line | Data |
---|---|---|
9a7cd639 FM |
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 | * File containing the course class. | |
19 | * | |
20 | * @package tool_uploadcourse | |
21 | * @copyright 2013 Frédéric Massart | |
22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
23 | */ | |
24 | ||
25 | defined('MOODLE_INTERNAL') || die(); | |
26 | ||
27 | /** | |
28 | * Course class. | |
29 | * | |
30 | * @package tool_uploadcourse | |
31 | * @copyright 2013 Frédéric Massart | |
32 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
33 | */ | |
34 | class tool_uploadcourse_course { | |
35 | ||
36 | /** Outcome of the process: creating the course */ | |
96aaf654 | 37 | const DO_CREATE = 1; |
9a7cd639 FM |
38 | |
39 | /** Outcome of the process: updating the course */ | |
96aaf654 | 40 | const DO_UPDATE = 2; |
9a7cd639 FM |
41 | |
42 | /** Outcome of the process: deleting the course */ | |
96aaf654 | 43 | const DO_DELETE = 3; |
9a7cd639 FM |
44 | |
45 | /** @var array final import data. */ | |
46 | protected $data = array(); | |
47 | ||
48 | /** @var array default values. */ | |
49 | protected $defaults = array(); | |
50 | ||
51 | /** @var array enrolment data. */ | |
52 | protected $enrolmentdata; | |
53 | ||
54 | /** @var array errors. */ | |
1d1898ac | 55 | protected $errors = array(); |
9a7cd639 FM |
56 | |
57 | /** @var array containing options passed from the processor. */ | |
58 | protected $importoptions = array(); | |
59 | ||
60 | /** @var int import mode. Matches tool_uploadcourse_processor::MODE_* */ | |
61 | protected $mode; | |
62 | ||
63 | /** @var array course import options. */ | |
64 | protected $options = array(); | |
65 | ||
96aaf654 FM |
66 | /** @var int constant value of self::DO_*, what to do with that course */ |
67 | protected $do; | |
9a7cd639 FM |
68 | |
69 | /** @var bool set to true once we have prepared the course */ | |
70 | protected $prepared = false; | |
71 | ||
72 | /** @var array course import data. */ | |
73 | protected $rawdata = array(); | |
74 | ||
75 | /** @var array restore directory. */ | |
76 | protected $restoredata; | |
77 | ||
78 | /** @var string course shortname. */ | |
79 | protected $shortname; | |
80 | ||
1d1898ac FM |
81 | /** @var array errors. */ |
82 | protected $statuses = array(); | |
83 | ||
9a7cd639 FM |
84 | /** @var int update mode. Matches tool_uploadcourse_processor::UPDATE_* */ |
85 | protected $updatemode; | |
86 | ||
87 | /** @var array fields allowed as course data. */ | |
88 | static protected $validfields = array('fullname', 'shortname', 'idnumber', 'category', 'visible', 'startdate', | |
89 | 'summary', 'format', 'theme', 'lang', 'newsitems', 'showgrades', 'showreports', 'legacyfiles', 'maxbytes', | |
90 | 'groupmode', 'groupmodeforce', 'groupmodeforce', 'enablecompletion'); | |
91 | ||
92 | /** @var array fields required on course creation. */ | |
93 | static protected $mandatoryfields = array('fullname', 'summary', 'category'); | |
94 | ||
95 | /** @var array fields which are considered as options. */ | |
96 | static protected $optionfields = array('delete' => false, 'rename' => null, 'backupfile' => null, | |
97 | 'templatecourse' => null, 'reset' => false); | |
98 | ||
99 | /** @var array options determining what can or cannot be done at an import level. */ | |
100 | static protected $importoptionsdefaults = array('canrename' => false, 'candelete' => false, 'canreset' => false, | |
101 | 'reset' => false, 'restoredir' => null, 'shortnametemplate' => null); | |
102 | ||
103 | /** | |
104 | * Constructor | |
105 | * | |
106 | * @param int $mode import mode, constant matching tool_uploadcourse_processor::MODE_* | |
107 | * @param int $updatemode update mode, constant matching tool_uploadcourse_processor::UPDATE_* | |
108 | * @param array $rawdata raw course data. | |
109 | * @param array $defaults default course data. | |
110 | * @param array $importoptions import options. | |
111 | */ | |
112 | public function __construct($mode, $updatemode, $rawdata, $defaults = array(), $importoptions = array()) { | |
113 | ||
114 | if ($mode !== tool_uploadcourse_processor::MODE_CREATE_NEW && | |
115 | $mode !== tool_uploadcourse_processor::MODE_CREATE_ALL && | |
116 | $mode !== tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE && | |
117 | $mode !== tool_uploadcourse_processor::MODE_UPDATE_ONLY) { | |
118 | throw new coding_exception('Incorrect mode.'); | |
119 | } else if ($updatemode !== tool_uploadcourse_processor::UPDATE_NOTHING && | |
120 | $updatemode !== tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY && | |
121 | $updatemode !== tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_OR_DEFAUTLS && | |
122 | $updatemode !== tool_uploadcourse_processor::UPDATE_MISSING_WITH_DATA_OR_DEFAUTLS) { | |
123 | throw new coding_exception('Incorrect update mode.'); | |
124 | } | |
125 | ||
126 | $this->mode = $mode; | |
127 | $this->updatemode = $updatemode; | |
128 | ||
129 | if (isset($rawdata['shortname'])) { | |
130 | $this->shortname = $rawdata['shortname']; | |
131 | } | |
132 | $this->rawdata = $rawdata; | |
133 | $this->defaults = $defaults; | |
134 | ||
135 | // Extract course options. | |
136 | foreach (self::$optionfields as $option => $default) { | |
137 | $this->options[$option] = isset($rawdata[$option]) ? $rawdata[$option] : $default; | |
138 | } | |
139 | ||
140 | // Import options. | |
141 | foreach (self::$importoptionsdefaults as $option => $default) { | |
142 | $this->importoptions[$option] = isset($importoptions[$option]) ? $importoptions[$option] : $default; | |
143 | } | |
144 | } | |
145 | ||
146 | /** | |
147 | * Does the mode allow for course creation? | |
148 | * | |
149 | * @return bool | |
150 | */ | |
151 | public function can_create() { | |
152 | return in_array($this->mode, array(tool_uploadcourse_processor::MODE_CREATE_ALL, | |
153 | tool_uploadcourse_processor::MODE_CREATE_NEW, | |
154 | tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE) | |
155 | ); | |
156 | } | |
157 | ||
158 | /** | |
159 | * Does the mode allow for course deletion? | |
160 | * | |
161 | * @return bool | |
162 | */ | |
163 | public function can_delete() { | |
164 | return $this->importoptions['candelete']; | |
165 | } | |
166 | ||
167 | /** | |
168 | * Does the mode only allow for course creation? | |
169 | * | |
170 | * @return bool | |
171 | */ | |
172 | public function can_only_create() { | |
173 | return in_array($this->mode, array(tool_uploadcourse_processor::MODE_CREATE_ALL, | |
174 | tool_uploadcourse_processor::MODE_CREATE_NEW)); | |
175 | } | |
176 | ||
177 | /** | |
178 | * Does the mode allow for course rename? | |
179 | * | |
180 | * @return bool | |
181 | */ | |
182 | public function can_rename() { | |
183 | return $this->importoptions['canrename']; | |
184 | } | |
185 | ||
186 | /** | |
187 | * Does the mode allow for course reset? | |
188 | * | |
189 | * @return bool | |
190 | */ | |
191 | public function can_reset() { | |
192 | return $this->importoptions['canreset']; | |
193 | } | |
194 | ||
195 | /** | |
196 | * Does the mode allow for course update? | |
197 | * | |
198 | * @return bool | |
199 | */ | |
200 | public function can_update() { | |
201 | return in_array($this->mode, array(tool_uploadcourse_processor::MODE_UPDATE_ONLY, | |
202 | tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE)) && $this->updatemode != tool_uploadcourse_processor::UPDATE_NOTHING; | |
203 | } | |
204 | ||
205 | /** | |
206 | * Can we use default values? | |
207 | * | |
208 | * @return bool | |
209 | */ | |
210 | public function can_use_defaults() { | |
211 | return in_array($this->updatemode, array(tool_uploadcourse_processor::UPDATE_MISSING_WITH_DATA_OR_DEFAUTLS, | |
212 | tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_OR_DEFAUTLS)); | |
213 | } | |
214 | ||
215 | /** | |
216 | * Delete the current course. | |
217 | * | |
218 | * @return bool | |
219 | */ | |
220 | protected function delete() { | |
221 | global $DB; | |
222 | $id = $DB->get_field_select('course', 'id', 'shortname = :shortname', array('shortname' => $this->shortname), MUST_EXIST); | |
223 | return delete_course($id, false); | |
224 | } | |
225 | ||
226 | /** | |
227 | * Log an error | |
228 | * | |
1d1898ac FM |
229 | * @param string $code error code. |
230 | * @param lang_string $message error message. | |
9a7cd639 FM |
231 | * @return void |
232 | */ | |
1d1898ac FM |
233 | protected function error($code, lang_string $message) { |
234 | if (array_key_exists($code, $this->errors)) { | |
235 | throw new coding_exception('Error code already defined'); | |
236 | } | |
237 | $this->errors[$code] = $message; | |
9a7cd639 FM |
238 | } |
239 | ||
240 | /** | |
241 | * Return whether the course exists or not. | |
242 | * | |
243 | * @return bool | |
244 | */ | |
245 | protected function exists($shortname = null) { | |
246 | global $DB; | |
247 | if (is_null($shortname)) { | |
248 | $shortname = $this->shortname; | |
249 | } | |
250 | if (!empty($shortname) || is_numeric($shortname)) { | |
251 | return $DB->record_exists('course', array('shortname' => $shortname)); | |
252 | } | |
253 | return false; | |
254 | } | |
255 | ||
256 | /** | |
257 | * Return the data that will be used upon saving. | |
258 | * | |
259 | * @return null|array | |
260 | */ | |
261 | public function get_data() { | |
262 | return $this->data; | |
263 | } | |
264 | ||
265 | /** | |
266 | * Return the errors found during preparation. | |
267 | * | |
268 | * @return array | |
269 | */ | |
270 | public function get_errors() { | |
271 | return $this->errors; | |
272 | } | |
273 | ||
274 | /** | |
275 | * Assemble the course data based on defaults. | |
276 | * | |
277 | * This returns the final data to be passed to create_course(). | |
278 | * | |
279 | * @param array $data current data. | |
280 | * @return array | |
281 | */ | |
282 | protected function get_final_create_data($data) { | |
283 | foreach (self::$validfields as $field) { | |
284 | if (!isset($data[$field]) && isset($this->defaults[$field])) { | |
285 | $data[$field] = $this->defaults[$field]; | |
286 | } | |
287 | } | |
288 | $data['shortname'] = $this->shortname; | |
289 | return $data; | |
290 | } | |
291 | ||
292 | /** | |
293 | * Assemble the course data based on defaults. | |
294 | * | |
295 | * This returns the final data to be passed to update_course(). | |
296 | * | |
297 | * @param array $data current data. | |
298 | * @param bool $usedefaults are defaults allowed? | |
299 | * @param bool $missingonly ignore fields which are already set. | |
300 | * @return array | |
301 | */ | |
302 | protected function get_final_update_data($data, $usedefaults = false, $missingonly = false) { | |
303 | global $DB; | |
304 | $newdata = array(); | |
305 | $existingdata = $DB->get_record('course', array('shortname' => $this->shortname)); | |
306 | foreach (self::$validfields as $field) { | |
307 | if ($missingonly) { | |
308 | if (!is_null($existingdata->$field) and $existingdata->$field !== '') { | |
309 | continue; | |
310 | } | |
311 | } | |
312 | if (isset($data[$field])) { | |
313 | $newdata[$field] = $data[$field]; | |
314 | } else if ($usedefaults && isset($this->defaults[$field])) { | |
315 | $newdata[$field] = $this->defaults[$field]; | |
316 | } | |
317 | } | |
318 | $newdata['id'] = $existingdata->id; | |
319 | return $newdata; | |
320 | } | |
321 | ||
322 | /** | |
323 | * Get the directory of the object to restore. | |
324 | * | |
1d1898ac | 325 | * @return string|false subdirectory in $CFG->tempdir/backup/... |
9a7cd639 FM |
326 | */ |
327 | protected function get_restore_content_dir() { | |
328 | $backupfile = null; | |
329 | $shortname = null; | |
330 | ||
331 | if (!empty($this->options['backupfile'])) { | |
332 | $backupfile = $this->options['backupfile']; | |
333 | } else if (!empty($this->options['templatecourse']) || is_numeric($this->options['templatecourse'])) { | |
334 | $shortname = $this->options['templatecourse']; | |
335 | } | |
336 | ||
1d1898ac FM |
337 | $errors = array(); |
338 | $dir = tool_uploadcourse_helper::get_restore_content_dir($backupfile, $shortname, $errors); | |
339 | if (!empty($errors)) { | |
340 | foreach ($errors as $key => $message) { | |
341 | $this->error($key, $message); | |
342 | } | |
343 | return false; | |
344 | } | |
345 | ||
9a7cd639 FM |
346 | if (empty($dir) && !empty($this->importoptions['restoredir'])) { |
347 | $dir = $this->importoptions['restoredir']; | |
348 | } | |
349 | ||
350 | return $dir; | |
351 | } | |
352 | ||
1d1898ac FM |
353 | /** |
354 | * Return the errors found during preparation. | |
355 | * | |
356 | * @return array | |
357 | */ | |
358 | public function get_statuses() { | |
359 | return $this->statuses; | |
360 | } | |
361 | ||
9a7cd639 FM |
362 | /** |
363 | * Return whether there were errors with this course. | |
364 | * | |
365 | * @return boolean | |
366 | */ | |
367 | public function has_errors() { | |
368 | return !empty($this->errors); | |
369 | } | |
370 | ||
371 | /** | |
372 | * Validates and prepares the data. | |
373 | * | |
374 | * @return bool false is any error occured. | |
375 | */ | |
376 | public function prepare() { | |
377 | $this->prepared = true; | |
378 | ||
379 | // Validate the shortname. | |
380 | if (!empty($this->shortname) || is_numeric($this->shortname)) { | |
381 | if ($this->shortname !== clean_param($this->shortname, PARAM_TEXT)) { | |
1d1898ac | 382 | $this->error('invalidshortname', new lang_string('invalidshortname', 'tool_uploadcourse')); |
9a7cd639 FM |
383 | return false; |
384 | } | |
385 | } | |
386 | ||
387 | $exists = $this->exists(); | |
388 | ||
389 | // Do we want to delete the course? | |
390 | if ($this->options['delete']) { | |
391 | if (!$exists) { | |
1d1898ac | 392 | $this->error('cannotdeletecoursenotexist', new lang_string('cannotdeletecoursenotexist', 'tool_uploadcourse')); |
9a7cd639 FM |
393 | return false; |
394 | } else if (!$this->can_delete()) { | |
1d1898ac | 395 | $this->error('coursedeletionnotallowed', new lang_string('coursedeletionnotallowed', 'tool_uploadcourse')); |
9a7cd639 FM |
396 | return false; |
397 | } | |
398 | ||
96aaf654 | 399 | $this->do = self::DO_DELETE; |
9a7cd639 FM |
400 | return true; |
401 | } | |
402 | ||
403 | // Can we create/update the course under those conditions? | |
404 | if ($exists) { | |
405 | if ($this->mode === tool_uploadcourse_processor::MODE_CREATE_NEW) { | |
1d1898ac | 406 | $this->error('courseexistsanduploadnotallowed', new lang_string('courseexistsanduploadnotallowed', 'tool_uploadcourse')); |
9a7cd639 FM |
407 | return false; |
408 | } | |
409 | } else { | |
410 | if (!$this->can_create()) { | |
1d1898ac FM |
411 | $this->error('coursedoesnotexistandcreatenotallowed', |
412 | new lang_string('coursedoesnotexistandcreatenotallowed', 'tool_uploadcourse')); | |
9a7cd639 FM |
413 | return false; |
414 | } | |
415 | } | |
416 | ||
417 | // Basic data. | |
418 | $coursedata = array(); | |
419 | foreach ($this->rawdata as $field => $value) { | |
420 | if (!in_array($field, self::$validfields)) { | |
421 | continue; | |
422 | } else if ($field == 'shortname') { | |
423 | // Let's leave it apart from now, use $this->shortname only. | |
424 | continue; | |
425 | } | |
426 | $coursedata[$field] = $value; | |
427 | } | |
428 | ||
429 | $mode = $this->mode; | |
430 | $updatemode = $this->updatemode; | |
431 | $usedefaults = $this->can_use_defaults(); | |
432 | ||
8383903f FM |
433 | // Resolve the category, and fail if not found. |
434 | $errors = array(); | |
435 | $catid = tool_uploadcourse_helper::resolve_category($this->rawdata, $errors); | |
436 | if (empty($errors)) { | |
437 | $coursedata['category'] = $catid; | |
438 | } else { | |
439 | foreach ($errors as $key => $message) { | |
440 | $this->error($key, $message); | |
441 | } | |
442 | return false; | |
443 | } | |
444 | ||
9a7cd639 FM |
445 | // If the course does not exist, or will be forced created. |
446 | if (!$exists || $mode === tool_uploadcourse_processor::MODE_CREATE_ALL) { | |
447 | ||
448 | // Mandatory fields upon creation. | |
1d1898ac | 449 | $errors = array(); |
9a7cd639 FM |
450 | foreach (self::$mandatoryfields as $field) { |
451 | if ((!isset($coursedata[$field]) || $coursedata[$field] === '') && | |
452 | (!isset($this->defaults[$field]) || $this->defaults[$field] === '')) { | |
1d1898ac | 453 | $errors[] = $field; |
9a7cd639 FM |
454 | } |
455 | } | |
1d1898ac FM |
456 | if (!empty($errors)) { |
457 | $this->error('missingmandatoryfields', new lang_string('missingmandatoryfields', 'tool_uploadcourse', | |
458 | implode(', ', $errors))); | |
9a7cd639 FM |
459 | return false; |
460 | } | |
461 | } | |
462 | ||
463 | // Should the course be renamed? | |
464 | if (!empty($this->options['rename']) || is_numeric($this->options['rename'])) { | |
1d1898ac FM |
465 | if (!$this->can_update()) { |
466 | $this->error('canonlyrenameinupdatemode', new lang_string('canonlyrenameinupdatemode', 'tool_uploadcourse')); | |
467 | return false; | |
468 | } else if (!$exists) { | |
469 | $this->error('cannotrenamecoursenotexist', new lang_string('cannotrenamecoursenotexist', 'tool_uploadcourse')); | |
9a7cd639 FM |
470 | return false; |
471 | } else if (!$this->can_rename()) { | |
1d1898ac | 472 | $this->error('courserenamingnotallowed', new lang_string('courserenamingnotallowed', 'tool_uploadcourse')); |
9a7cd639 FM |
473 | return false; |
474 | } else if ($this->options['rename'] !== clean_param($this->options['rename'], PARAM_TEXT)) { | |
1d1898ac | 475 | $this->error('invalidshortname', new lang_string('invalidshortname', 'tool_uploadcourse')); |
9a7cd639 FM |
476 | return false; |
477 | } else if ($this->exists($this->options['rename'])) { | |
1d1898ac FM |
478 | $this->error('cannotrenameshortnamealreadyinuse', |
479 | new lang_string('cannotrenameshortnamealreadyinuse', 'tool_uploadcourse')); | |
9a7cd639 FM |
480 | return false; |
481 | } else if (isset($coursedata['idnumber']) && | |
482 | $DB->count_records_select('course', 'idnumber = :idn AND shortname != :sn', | |
483 | array('idn' => $coursedata['idnumber'], 'sn' => $this->shortname)) > 0) { | |
1d1898ac | 484 | $this->error('cannotrenameidnumberconflict', new lang_string('cannotrenameidnumberconflict', 'tool_uploadcourse')); |
9a7cd639 FM |
485 | return false; |
486 | } | |
487 | $coursedata['shortname'] = $this->options['rename']; | |
1d1898ac FM |
488 | $this->status('courserenamed', new lang_string('courserenamed', 'tool_uploadcourse', |
489 | array('from' => $this->shortname, 'to' => $coursedata['shortname']))); | |
9a7cd639 FM |
490 | } |
491 | ||
492 | // Should we generate a shortname? | |
493 | if (empty($this->shortname) && !is_numeric($this->shortname)) { | |
494 | if (empty($this->importoptions['shortnametemplate'])) { | |
1d1898ac | 495 | $this->error('missingshortnamenotemplate', new lang_string('missingshortnamenotemplate', 'tool_uploadcourse')); |
9a7cd639 FM |
496 | return false; |
497 | } else if (!$this->can_only_create()) { | |
1d1898ac FM |
498 | $this->error('cannotgenerateshortnameupdatemode', |
499 | new lang_string('cannotgenerateshortnameupdatemode', 'tool_uploadcourse')); | |
9a7cd639 FM |
500 | return false; |
501 | } else { | |
502 | $newshortname = tool_uploadcourse_helper::generate_shortname($coursedata, $this->importoptions['shortnametemplate']); | |
503 | if (is_null($newshortname)) { | |
1d1898ac | 504 | $this->error('generatedshortnameinvalid', new lang_string('generatedshortnameinvalid', 'tool_uploadcourse')); |
9a7cd639 FM |
505 | return false; |
506 | } else if ($this->exists($newshortname)) { | |
507 | if ($mode === tool_uploadcourse_processor::MODE_CREATE_NEW) { | |
1d1898ac FM |
508 | $this->error('generatedshortnamealreadyinuse', |
509 | new lang_string('generatedshortnamealreadyinuse', 'tool_uploadcourse')); | |
9a7cd639 FM |
510 | return false; |
511 | } | |
512 | $exists = true; | |
513 | } | |
5b2f7718 FM |
514 | $this->status('courseshortnamegenerated', new lang_string('courseshortnamegenerated', 'tool_uploadcourse', |
515 | $newshortname)); | |
9a7cd639 FM |
516 | $this->shortname = $newshortname; |
517 | } | |
518 | } | |
519 | ||
520 | // If exists, but we only want to create courses, increment the shortname. | |
521 | if ($exists && $mode === tool_uploadcourse_processor::MODE_CREATE_ALL) { | |
522 | $original = $this->shortname; | |
523 | $this->shortname = tool_uploadcourse_helper::increment_shortname($this->shortname); | |
524 | $exists = false; | |
525 | if ($this->shortname != $original) { | |
1d1898ac FM |
526 | $this->status('courseshortnameincremented', new lang_string('courseshortnameincremented', 'tool_uploadcourse', |
527 | array('from' => $original, 'to' => $this->shortname))); | |
9a7cd639 FM |
528 | if (isset($coursedata['idnumber'])) { |
529 | $originalidn = $coursedata['idnumber']; | |
8383903f | 530 | $coursedata['idnumber'] = tool_uploadcourse_helper::increment_idnumber($coursedata['idnumber']); |
9a7cd639 | 531 | if ($originalidn != $coursedata['idnumber']) { |
1d1898ac FM |
532 | $this->status('courseidnumberincremented', new lang_string('courseidnumberincremented', 'tool_uploadcourse', |
533 | array('from' => $originalidn, 'to' => $coursedata['idnumber']))); | |
9a7cd639 FM |
534 | } |
535 | } | |
536 | } | |
537 | } | |
538 | ||
539 | // Ultimate check mode vs. existence. | |
540 | switch ($mode) { | |
541 | case tool_uploadcourse_processor::MODE_CREATE_NEW: | |
542 | case tool_uploadcourse_processor::MODE_CREATE_ALL: | |
543 | if ($exists) { | |
1d1898ac FM |
544 | $this->error('courseexistsanduploadnotallowed', |
545 | new lang_string('courseexistsanduploadnotallowed', 'tool_uploadcourse')); | |
9a7cd639 FM |
546 | return false; |
547 | } | |
548 | break; | |
549 | case tool_uploadcourse_processor::MODE_UPDATE_ONLY: | |
550 | if (!$exists) { | |
1d1898ac FM |
551 | $this->error('coursedoesnotexistandcreatenotallowed', |
552 | new lang_string('coursedoesnotexistandcreatenotallowed', 'tool_uploadcourse')); | |
9a7cd639 FM |
553 | return false; |
554 | } | |
555 | // No break! | |
556 | case tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE: | |
557 | if ($exists) { | |
558 | if ($updatemode === tool_uploadcourse_processor::UPDATE_NOTHING) { | |
1d1898ac FM |
559 | $this->error('updatemodedoessettonothing', |
560 | new lang_string('updatemodedoessettonothing', 'tool_uploadcourse')); | |
9a7cd639 FM |
561 | return false; |
562 | } | |
563 | } | |
564 | break; | |
565 | default: | |
566 | // O_o Huh?! This should really never happen here! | |
1d1898ac | 567 | $this->error('unknownimportmode', new lang_string('unknownimportmode', 'tool_uploadcourse')); |
9a7cd639 FM |
568 | return false; |
569 | } | |
570 | ||
9a7cd639 FM |
571 | // Get final data. |
572 | if ($exists) { | |
573 | $missingonly = ($updatemode === tool_uploadcourse_processor::UPDATE_MISSING_WITH_DATA_OR_DEFAUTLS); | |
574 | $coursedata = $this->get_final_update_data($coursedata, $usedefaults, $missingonly); | |
96aaf654 | 575 | $this->do = self::DO_UPDATE; |
9a7cd639 FM |
576 | } else { |
577 | $coursedata = $this->get_final_create_data($coursedata); | |
96aaf654 | 578 | $this->do = self::DO_CREATE; |
9a7cd639 FM |
579 | } |
580 | ||
581 | // Course start date. | |
582 | if (!empty($coursedata['startdate'])) { | |
583 | $coursedata['startdate'] = strtotime($coursedata['startdate']); | |
584 | } | |
585 | ||
586 | // Add role renaming. | |
1d1898ac FM |
587 | $errors = array(); |
588 | $rolenames = tool_uploadcourse_helper::get_role_names($this->rawdata, $errors); | |
589 | if (!empty($errors)) { | |
590 | foreach ($errors as $key => $message) { | |
591 | $this->error($key, $message); | |
592 | } | |
593 | return false; | |
594 | } | |
595 | foreach ($rolenames as $rolekey => $rolename) { | |
9a7cd639 FM |
596 | $coursedata[$rolekey] = $rolename; |
597 | } | |
598 | ||
1d1898ac | 599 | // Some validation. |
9a7cd639 | 600 | if (!empty($coursedata['format']) && !in_array($coursedata['format'], tool_uploadcourse_helper::get_course_formats())) { |
1d1898ac FM |
601 | $this->error('invalidcourseformat', new lang_string('invalidcourseformat', 'tool_uploadcourse')); |
602 | return false; | |
9a7cd639 FM |
603 | } |
604 | ||
605 | // Saving data. | |
606 | $this->data = $coursedata; | |
607 | $this->enrolmentdata = tool_uploadcourse_helper::get_enrolment_data($this->rawdata); | |
608 | ||
609 | // Restore data. | |
610 | // TODO log warnings. | |
611 | $this->restoredata = $this->get_restore_content_dir(); | |
612 | ||
613 | // We cannot | |
614 | if ($this->importoptions['reset'] || $this->options['reset']) { | |
96aaf654 | 615 | if ($this->do !== self::DO_UPDATE) { |
1d1898ac FM |
616 | $this->error('canonlyresetcourseinupdatemode', |
617 | new lang_string('canonlyresetcourseinupdatemode', 'tool_uploadcourse')); | |
9a7cd639 FM |
618 | return false; |
619 | } else if (!$this->can_reset()) { | |
1d1898ac | 620 | $this->error('courseresetnotallowed', new lang_string('courseresetnotallowed', 'tool_uploadcourse')); |
9a7cd639 FM |
621 | return false; |
622 | } | |
623 | } | |
624 | ||
625 | return true; | |
626 | } | |
627 | ||
628 | /** | |
629 | * Proceed with the import of the course. | |
630 | * | |
631 | * @return void | |
632 | */ | |
633 | public function proceed() { | |
634 | global $CFG, $USER; | |
635 | ||
636 | if (!$this->prepared) { | |
637 | throw new coding_exception('The course has not been prepared.'); | |
638 | } else if ($this->has_errors()) { | |
639 | throw new moodle_exception('Cannot proceed, errors were detected.'); | |
640 | } | |
641 | ||
96aaf654 | 642 | if ($this->do === self::DO_DELETE) { |
1d1898ac FM |
643 | if ($this->delete()) { |
644 | $this->status('coursedeleted', new lang_string('coursedeleted', 'tool_uploadcourse')); | |
645 | } else { | |
646 | $this->error('errorwhiledeletingcourse', new lang_string('errorwhiledeletingcourse', 'tool_uploadcourse')); | |
647 | } | |
8383903f | 648 | return true; |
96aaf654 | 649 | } else if ($this->do === self::DO_CREATE) { |
9a7cd639 | 650 | $course = create_course((object) $this->data); |
1d1898ac | 651 | $this->status('coursecreated', new lang_string('coursecreated', 'tool_uploadcourse')); |
96aaf654 | 652 | } else if ($this->do === self::DO_UPDATE) { |
9a7cd639 FM |
653 | $course = (object) $this->data; |
654 | update_course($course); | |
1d1898ac | 655 | $this->status('courseupdated', new lang_string('courseupdated', 'tool_uploadcourse')); |
9a7cd639 FM |
656 | } else { |
657 | // Strangely the outcome has not been defined, or is unknown! | |
658 | throw new coding_exception('Unknown outcome!'); | |
659 | } | |
660 | ||
661 | // Restore a course. | |
662 | if (!empty($this->restoredata)) { | |
663 | $rc = new restore_controller($this->restoredata, $course->id, backup::INTERACTIVE_NO, | |
664 | backup::MODE_IMPORT, $USER->id, backup::TARGET_CURRENT_ADDING); | |
665 | ||
666 | // Check if the format conversion must happen first. | |
667 | if ($rc->get_status() == backup::STATUS_REQUIRE_CONV) { | |
668 | $rc->convert(); | |
669 | } | |
670 | if ($rc->execute_precheck()) { | |
671 | $rc->execute_plan(); | |
1d1898ac | 672 | $this->status('courserestored', new lang_string('courserestored', 'tool_uploadcourse')); |
9a7cd639 | 673 | } else { |
1d1898ac | 674 | $this->error('errorwhilerestoringcourse', new lang_string('errorwhilerestoringthecourse', 'tool_uploadcourse')); |
9a7cd639 FM |
675 | } |
676 | $rc->destroy(); | |
677 | } | |
678 | ||
679 | // Proceed with enrolment data. | |
680 | $this->process_enrolment_data($course); | |
681 | ||
682 | // Reset the course. | |
683 | if ($this->importoptions['reset'] || $this->options['reset']) { | |
96aaf654 | 684 | if ($this->do === self::DO_UPDATE && $this->can_reset()) { |
9a7cd639 | 685 | $this->reset($course); |
1d1898ac | 686 | $this->status('coursereset', new lang_string('coursereset', 'tool_uploadcourse')); |
9a7cd639 FM |
687 | } |
688 | } | |
689 | ||
690 | // Mark context as dirty. | |
691 | $context = context_course::instance($course->id); | |
692 | $context->mark_dirty(); | |
693 | } | |
694 | ||
695 | /** | |
696 | * Add the enrolment data for the course. | |
697 | * | |
698 | * @param object $course course record. | |
699 | * @return void | |
700 | */ | |
701 | protected function process_enrolment_data($course) { | |
702 | global $DB; | |
703 | ||
704 | $enrolmentdata = $this->enrolmentdata; | |
705 | if (empty($enrolmentdata)) { | |
706 | return; | |
707 | } | |
708 | ||
709 | $enrolmentplugins = tool_uploadcourse_helper::get_enrolment_plugins(); | |
710 | $instances = enrol_get_instances($course->id, false); | |
711 | foreach ($enrolmentdata as $enrolmethod => $method) { | |
712 | ||
713 | $instance = null; | |
714 | foreach ($instances as $i) { | |
715 | if ($i->enrol == $enrolmethod) { | |
716 | $instance = $i; | |
717 | break; | |
718 | } | |
719 | } | |
720 | ||
721 | $todelete = isset($method['delete']) && $method['delete']; | |
722 | $todisable = isset($method['disable']) && $method['disable']; | |
723 | unset($method['delete']); | |
724 | unset($method['disable']); | |
725 | ||
726 | if (!empty($instance) && $todelete) { | |
727 | // Remove the enrolment method. | |
728 | foreach ($instances as $instance) { | |
729 | if ($instance->enrol == $enrolmethod) { | |
730 | $plugin = $enrolmentplugins[$instance->enrol]; | |
731 | $plugin->delete_instance($instance); | |
732 | break; | |
733 | } | |
734 | } | |
735 | } else if (!empty($instance) && $todisable) { | |
736 | // Disable the enrolment. | |
737 | foreach ($instances as $instance) { | |
738 | if ($instance->enrol == $enrolmethod) { | |
739 | $plugin = $enrolmentplugins[$instance->enrol]; | |
740 | $plugin->update_status($instance, ENROL_INSTANCE_DISABLED); | |
741 | $enrol_updated = true; | |
742 | break; | |
743 | } | |
744 | } | |
745 | } else { | |
746 | $plugin = null; | |
747 | if (empty($instance)) { | |
748 | $plugin = $enrolmentplugins[$enrolmethod]; | |
749 | $instance = new stdClass(); | |
750 | $instance->id = $plugin->add_default_instance($course); | |
751 | $instance->roleid = $plugin->get_config('roleid'); | |
752 | $instance->status = ENROL_INSTANCE_ENABLED; | |
753 | } else { | |
754 | $plugin = $enrolmentplugins[$instance->enrol]; | |
755 | $plugin->update_status($instance, ENROL_INSTANCE_ENABLED); | |
756 | } | |
757 | ||
758 | // Now update values. | |
759 | foreach ($method as $k => $v) { | |
760 | $instance->{$k} = $v; | |
761 | } | |
762 | ||
763 | // Sort out the start, end and date. | |
764 | $instance->enrolstartdate = (isset($method['startdate']) ? strtotime($method['startdate']) : 0); | |
765 | $instance->enrolenddate = (isset($method['enddate']) ? strtotime($method['enddate']) : 0); | |
766 | ||
767 | // Is the enrolment period set? | |
768 | if (isset($method['enrolperiod']) && ! empty($method['enrolperiod'])) { | |
769 | if (preg_match('/^\d+$/', $method['enrolperiod'])) { | |
770 | $method['enrolperiod'] = (int) $method['enrolperiod']; | |
771 | } else { | |
772 | // Try and convert period to seconds. | |
773 | $method['enrolperiod'] = strtotime('1970-01-01 GMT + ' . $method['enrolperiod']); | |
774 | } | |
775 | $instance->enrolperiod = $method['enrolperiod']; | |
776 | } | |
777 | if ($instance->enrolstartdate > 0 && isset($method['enrolperiod'])) { | |
778 | $instance->enrolenddate = $instance->enrolstartdate + $method['enrolperiod']; | |
779 | } | |
780 | if ($instance->enrolenddate > 0) { | |
781 | $instance->enrolperiod = $instance->enrolenddate - $instance->enrolstartdate; | |
782 | } | |
783 | if ($instance->enrolenddate < $instance->enrolstartdate) { | |
784 | $instance->enrolenddate = $instance->enrolstartdate; | |
785 | } | |
786 | ||
787 | // Sort out the given role. This does not filter the roles allowed in the course. | |
788 | if (isset($method['role'])) { | |
789 | $roleids = tool_uploadcourse_helper::get_role_ids(); | |
790 | if (isset($roleids[$method['role']])) { | |
791 | $instance->roleid = $roleids[$method['role']]; | |
792 | } | |
793 | } | |
794 | ||
795 | $instance->timemodified = time(); | |
796 | $DB->update_record('enrol', $instance); | |
797 | } | |
798 | } | |
799 | } | |
800 | ||
801 | /** | |
802 | * Reset the current course. | |
803 | * | |
804 | * This does not reset any of the content of the activities. | |
805 | * | |
806 | * @return array status array of array component, item, error. | |
807 | */ | |
808 | protected function reset($course) { | |
809 | global $DB; | |
810 | ||
811 | $resetdata = new stdClass(); | |
812 | $resetdata->id = $course->id; | |
813 | $resetdata->reset_start_date = time(); | |
814 | $resetdata->reset_logs = true; | |
815 | $resetdata->reset_events = true; | |
816 | $resetdata->reset_notes = true; | |
817 | $resetdata->delete_blog_associations = true; | |
818 | $resetdata->reset_completion = true; | |
819 | $resetdata->reset_roles_overrides = true; | |
820 | $resetdata->reset_roles_local = true; | |
821 | $resetdata->reset_groups_members = true; | |
822 | $resetdata->reset_groups_remove = true; | |
823 | $resetdata->reset_groupings_members = true; | |
824 | $resetdata->reset_groupings_remove = true; | |
825 | $resetdata->reset_gradebook_items = true; | |
826 | $resetdata->reset_gradebook_grades = true; | |
827 | $resetdata->reset_comments = true; | |
828 | ||
829 | if (empty($course->startdate)) { | |
830 | $course->startdate = $DB->get_field_select('course', 'startdate', 'id = :id', array('id' => $course->id)); | |
831 | } | |
832 | $resetdata->reset_start_date_old = $course->startdate; | |
833 | ||
834 | // Add roles. | |
835 | $roles = tool_uploadcourse_helper::get_role_ids(); | |
836 | $resetdata->unenrol_users = array_values($roles); | |
837 | $resetdata->unenrol_users[] = 0; // Enrolled without role. | |
838 | ||
839 | return reset_course_userdata($resetdata); | |
840 | } | |
841 | ||
1d1898ac FM |
842 | /** |
843 | * Log a status | |
844 | * | |
845 | * @param string $code status code. | |
846 | * @param lang_string $message status message. | |
847 | * @return void | |
848 | */ | |
849 | protected function status($code, lang_string $message) { | |
850 | if (array_key_exists($code, $this->statuses)) { | |
851 | throw new coding_exception('Status code already defined'); | |
852 | } | |
853 | $this->statuses[$code] = $message; | |
854 | } | |
855 | ||
9a7cd639 | 856 | } |