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(); | |
e0164eb3 FM |
26 | require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php'); |
27 | require_once($CFG->dirroot . '/course/lib.php'); | |
9a7cd639 FM |
28 | |
29 | /** | |
30 | * Course class. | |
31 | * | |
32 | * @package tool_uploadcourse | |
33 | * @copyright 2013 Frédéric Massart | |
34 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
35 | */ | |
36 | class tool_uploadcourse_course { | |
37 | ||
38 | /** Outcome of the process: creating the course */ | |
96aaf654 | 39 | const DO_CREATE = 1; |
9a7cd639 FM |
40 | |
41 | /** Outcome of the process: updating the course */ | |
96aaf654 | 42 | const DO_UPDATE = 2; |
9a7cd639 FM |
43 | |
44 | /** Outcome of the process: deleting the course */ | |
96aaf654 | 45 | const DO_DELETE = 3; |
9a7cd639 FM |
46 | |
47 | /** @var array final import data. */ | |
48 | protected $data = array(); | |
49 | ||
50 | /** @var array default values. */ | |
51 | protected $defaults = array(); | |
52 | ||
53 | /** @var array enrolment data. */ | |
54 | protected $enrolmentdata; | |
55 | ||
56 | /** @var array errors. */ | |
1d1898ac | 57 | protected $errors = array(); |
9a7cd639 | 58 | |
1e3e4efd FM |
59 | /** @var int the ID of the course that had been processed. */ |
60 | protected $id; | |
61 | ||
9a7cd639 FM |
62 | /** @var array containing options passed from the processor. */ |
63 | protected $importoptions = array(); | |
64 | ||
65 | /** @var int import mode. Matches tool_uploadcourse_processor::MODE_* */ | |
66 | protected $mode; | |
67 | ||
68 | /** @var array course import options. */ | |
69 | protected $options = array(); | |
70 | ||
96aaf654 FM |
71 | /** @var int constant value of self::DO_*, what to do with that course */ |
72 | protected $do; | |
9a7cd639 FM |
73 | |
74 | /** @var bool set to true once we have prepared the course */ | |
75 | protected $prepared = false; | |
76 | ||
1e3e4efd FM |
77 | /** @var bool set to true once we have started the process of the course */ |
78 | protected $processstarted = false; | |
79 | ||
9a7cd639 FM |
80 | /** @var array course import data. */ |
81 | protected $rawdata = array(); | |
82 | ||
83 | /** @var array restore directory. */ | |
84 | protected $restoredata; | |
85 | ||
86 | /** @var string course shortname. */ | |
87 | protected $shortname; | |
88 | ||
1d1898ac FM |
89 | /** @var array errors. */ |
90 | protected $statuses = array(); | |
91 | ||
9a7cd639 FM |
92 | /** @var int update mode. Matches tool_uploadcourse_processor::UPDATE_* */ |
93 | protected $updatemode; | |
94 | ||
95 | /** @var array fields allowed as course data. */ | |
fbcdb0d7 | 96 | static protected $validfields = array('fullname', 'shortname', 'idnumber', 'category', 'visible', 'startdate', 'enddate', |
9a7cd639 | 97 | 'summary', 'format', 'theme', 'lang', 'newsitems', 'showgrades', 'showreports', 'legacyfiles', 'maxbytes', |
b1ed151f | 98 | 'groupmode', 'groupmodeforce', 'enablecompletion'); |
9a7cd639 FM |
99 | |
100 | /** @var array fields required on course creation. */ | |
a1af15de | 101 | static protected $mandatoryfields = array('fullname', 'category'); |
9a7cd639 FM |
102 | |
103 | /** @var array fields which are considered as options. */ | |
104 | static protected $optionfields = array('delete' => false, 'rename' => null, 'backupfile' => null, | |
105 | 'templatecourse' => null, 'reset' => false); | |
106 | ||
107 | /** @var array options determining what can or cannot be done at an import level. */ | |
108 | static protected $importoptionsdefaults = array('canrename' => false, 'candelete' => false, 'canreset' => false, | |
109 | 'reset' => false, 'restoredir' => null, 'shortnametemplate' => null); | |
110 | ||
111 | /** | |
112 | * Constructor | |
113 | * | |
114 | * @param int $mode import mode, constant matching tool_uploadcourse_processor::MODE_* | |
115 | * @param int $updatemode update mode, constant matching tool_uploadcourse_processor::UPDATE_* | |
116 | * @param array $rawdata raw course data. | |
117 | * @param array $defaults default course data. | |
118 | * @param array $importoptions import options. | |
119 | */ | |
120 | public function __construct($mode, $updatemode, $rawdata, $defaults = array(), $importoptions = array()) { | |
121 | ||
122 | if ($mode !== tool_uploadcourse_processor::MODE_CREATE_NEW && | |
123 | $mode !== tool_uploadcourse_processor::MODE_CREATE_ALL && | |
124 | $mode !== tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE && | |
125 | $mode !== tool_uploadcourse_processor::MODE_UPDATE_ONLY) { | |
126 | throw new coding_exception('Incorrect mode.'); | |
127 | } else if ($updatemode !== tool_uploadcourse_processor::UPDATE_NOTHING && | |
128 | $updatemode !== tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY && | |
129 | $updatemode !== tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_OR_DEFAUTLS && | |
130 | $updatemode !== tool_uploadcourse_processor::UPDATE_MISSING_WITH_DATA_OR_DEFAUTLS) { | |
131 | throw new coding_exception('Incorrect update mode.'); | |
132 | } | |
133 | ||
134 | $this->mode = $mode; | |
135 | $this->updatemode = $updatemode; | |
136 | ||
137 | if (isset($rawdata['shortname'])) { | |
138 | $this->shortname = $rawdata['shortname']; | |
139 | } | |
140 | $this->rawdata = $rawdata; | |
141 | $this->defaults = $defaults; | |
142 | ||
143 | // Extract course options. | |
144 | foreach (self::$optionfields as $option => $default) { | |
145 | $this->options[$option] = isset($rawdata[$option]) ? $rawdata[$option] : $default; | |
146 | } | |
147 | ||
148 | // Import options. | |
149 | foreach (self::$importoptionsdefaults as $option => $default) { | |
150 | $this->importoptions[$option] = isset($importoptions[$option]) ? $importoptions[$option] : $default; | |
151 | } | |
152 | } | |
153 | ||
154 | /** | |
155 | * Does the mode allow for course creation? | |
156 | * | |
157 | * @return bool | |
158 | */ | |
159 | public function can_create() { | |
160 | return in_array($this->mode, array(tool_uploadcourse_processor::MODE_CREATE_ALL, | |
161 | tool_uploadcourse_processor::MODE_CREATE_NEW, | |
162 | tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE) | |
163 | ); | |
164 | } | |
165 | ||
166 | /** | |
167 | * Does the mode allow for course deletion? | |
168 | * | |
169 | * @return bool | |
170 | */ | |
171 | public function can_delete() { | |
172 | return $this->importoptions['candelete']; | |
173 | } | |
174 | ||
175 | /** | |
176 | * Does the mode only allow for course creation? | |
177 | * | |
178 | * @return bool | |
179 | */ | |
180 | public function can_only_create() { | |
181 | return in_array($this->mode, array(tool_uploadcourse_processor::MODE_CREATE_ALL, | |
182 | tool_uploadcourse_processor::MODE_CREATE_NEW)); | |
183 | } | |
184 | ||
185 | /** | |
186 | * Does the mode allow for course rename? | |
187 | * | |
188 | * @return bool | |
189 | */ | |
190 | public function can_rename() { | |
191 | return $this->importoptions['canrename']; | |
192 | } | |
193 | ||
194 | /** | |
195 | * Does the mode allow for course reset? | |
196 | * | |
197 | * @return bool | |
198 | */ | |
199 | public function can_reset() { | |
200 | return $this->importoptions['canreset']; | |
201 | } | |
202 | ||
203 | /** | |
204 | * Does the mode allow for course update? | |
205 | * | |
206 | * @return bool | |
207 | */ | |
208 | public function can_update() { | |
3f57c87e FM |
209 | return in_array($this->mode, |
210 | array( | |
211 | tool_uploadcourse_processor::MODE_UPDATE_ONLY, | |
212 | tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE) | |
213 | ) && $this->updatemode != tool_uploadcourse_processor::UPDATE_NOTHING; | |
9a7cd639 FM |
214 | } |
215 | ||
216 | /** | |
217 | * Can we use default values? | |
218 | * | |
219 | * @return bool | |
220 | */ | |
221 | public function can_use_defaults() { | |
222 | return in_array($this->updatemode, array(tool_uploadcourse_processor::UPDATE_MISSING_WITH_DATA_OR_DEFAUTLS, | |
223 | tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_OR_DEFAUTLS)); | |
224 | } | |
225 | ||
226 | /** | |
227 | * Delete the current course. | |
228 | * | |
229 | * @return bool | |
230 | */ | |
231 | protected function delete() { | |
232 | global $DB; | |
3f57c87e FM |
233 | $this->id = $DB->get_field_select('course', 'id', 'shortname = :shortname', |
234 | array('shortname' => $this->shortname), MUST_EXIST); | |
1e3e4efd | 235 | return delete_course($this->id, false); |
9a7cd639 FM |
236 | } |
237 | ||
238 | /** | |
239 | * Log an error | |
240 | * | |
1d1898ac FM |
241 | * @param string $code error code. |
242 | * @param lang_string $message error message. | |
9a7cd639 FM |
243 | * @return void |
244 | */ | |
1d1898ac FM |
245 | protected function error($code, lang_string $message) { |
246 | if (array_key_exists($code, $this->errors)) { | |
247 | throw new coding_exception('Error code already defined'); | |
248 | } | |
249 | $this->errors[$code] = $message; | |
9a7cd639 FM |
250 | } |
251 | ||
252 | /** | |
253 | * Return whether the course exists or not. | |
254 | * | |
3f57c87e | 255 | * @param string $shortname the shortname to use to check if the course exists. Falls back on $this->shortname if empty. |
9a7cd639 FM |
256 | * @return bool |
257 | */ | |
258 | protected function exists($shortname = null) { | |
259 | global $DB; | |
260 | if (is_null($shortname)) { | |
261 | $shortname = $this->shortname; | |
262 | } | |
263 | if (!empty($shortname) || is_numeric($shortname)) { | |
264 | return $DB->record_exists('course', array('shortname' => $shortname)); | |
265 | } | |
266 | return false; | |
267 | } | |
268 | ||
269 | /** | |
270 | * Return the data that will be used upon saving. | |
271 | * | |
272 | * @return null|array | |
273 | */ | |
274 | public function get_data() { | |
275 | return $this->data; | |
276 | } | |
277 | ||
278 | /** | |
279 | * Return the errors found during preparation. | |
280 | * | |
281 | * @return array | |
282 | */ | |
283 | public function get_errors() { | |
284 | return $this->errors; | |
285 | } | |
286 | ||
d62fc08e PH |
287 | /** |
288 | * Return array of valid fields for default values | |
289 | * | |
290 | * @return array | |
291 | */ | |
292 | protected function get_valid_fields() { | |
293 | return array_merge(self::$validfields, \tool_uploadcourse_helper::get_custom_course_field_names()); | |
294 | } | |
295 | ||
9a7cd639 FM |
296 | /** |
297 | * Assemble the course data based on defaults. | |
298 | * | |
299 | * This returns the final data to be passed to create_course(). | |
300 | * | |
301 | * @param array $data current data. | |
302 | * @return array | |
303 | */ | |
304 | protected function get_final_create_data($data) { | |
d62fc08e | 305 | foreach ($this->get_valid_fields() as $field) { |
9a7cd639 FM |
306 | if (!isset($data[$field]) && isset($this->defaults[$field])) { |
307 | $data[$field] = $this->defaults[$field]; | |
308 | } | |
309 | } | |
310 | $data['shortname'] = $this->shortname; | |
311 | return $data; | |
312 | } | |
313 | ||
314 | /** | |
315 | * Assemble the course data based on defaults. | |
316 | * | |
317 | * This returns the final data to be passed to update_course(). | |
318 | * | |
319 | * @param array $data current data. | |
320 | * @param bool $usedefaults are defaults allowed? | |
321 | * @param bool $missingonly ignore fields which are already set. | |
322 | * @return array | |
323 | */ | |
324 | protected function get_final_update_data($data, $usedefaults = false, $missingonly = false) { | |
325 | global $DB; | |
326 | $newdata = array(); | |
327 | $existingdata = $DB->get_record('course', array('shortname' => $this->shortname)); | |
d62fc08e | 328 | foreach ($this->get_valid_fields() as $field) { |
9a7cd639 | 329 | if ($missingonly) { |
d62fc08e | 330 | if (isset($existingdata->$field) and $existingdata->$field !== '') { |
9a7cd639 FM |
331 | continue; |
332 | } | |
333 | } | |
334 | if (isset($data[$field])) { | |
335 | $newdata[$field] = $data[$field]; | |
336 | } else if ($usedefaults && isset($this->defaults[$field])) { | |
337 | $newdata[$field] = $this->defaults[$field]; | |
338 | } | |
339 | } | |
340 | $newdata['id'] = $existingdata->id; | |
341 | return $newdata; | |
342 | } | |
343 | ||
1e3e4efd FM |
344 | /** |
345 | * Return the ID of the processed course. | |
346 | * | |
347 | * @return int|null | |
348 | */ | |
349 | public function get_id() { | |
350 | if (!$this->processstarted) { | |
351 | throw new coding_exception('The course has not been processed yet!'); | |
352 | } | |
353 | return $this->id; | |
354 | } | |
355 | ||
9a7cd639 FM |
356 | /** |
357 | * Get the directory of the object to restore. | |
358 | * | |
ef844148 | 359 | * @return string|false|null subdirectory in $CFG->backuptempdir/..., false when an error occured |
7bfc9edd | 360 | * and null when there is simply nothing. |
9a7cd639 FM |
361 | */ |
362 | protected function get_restore_content_dir() { | |
363 | $backupfile = null; | |
364 | $shortname = null; | |
365 | ||
366 | if (!empty($this->options['backupfile'])) { | |
367 | $backupfile = $this->options['backupfile']; | |
368 | } else if (!empty($this->options['templatecourse']) || is_numeric($this->options['templatecourse'])) { | |
369 | $shortname = $this->options['templatecourse']; | |
370 | } | |
371 | ||
1d1898ac FM |
372 | $errors = array(); |
373 | $dir = tool_uploadcourse_helper::get_restore_content_dir($backupfile, $shortname, $errors); | |
374 | if (!empty($errors)) { | |
375 | foreach ($errors as $key => $message) { | |
376 | $this->error($key, $message); | |
377 | } | |
378 | return false; | |
7bfc9edd FM |
379 | } else if ($dir === false) { |
380 | // We want to return null when nothing was wrong, but nothing was found. | |
381 | $dir = null; | |
1d1898ac FM |
382 | } |
383 | ||
9a7cd639 FM |
384 | if (empty($dir) && !empty($this->importoptions['restoredir'])) { |
385 | $dir = $this->importoptions['restoredir']; | |
386 | } | |
387 | ||
388 | return $dir; | |
389 | } | |
390 | ||
1d1898ac FM |
391 | /** |
392 | * Return the errors found during preparation. | |
393 | * | |
394 | * @return array | |
395 | */ | |
396 | public function get_statuses() { | |
397 | return $this->statuses; | |
398 | } | |
399 | ||
9a7cd639 FM |
400 | /** |
401 | * Return whether there were errors with this course. | |
402 | * | |
403 | * @return boolean | |
404 | */ | |
405 | public function has_errors() { | |
406 | return !empty($this->errors); | |
407 | } | |
408 | ||
409 | /** | |
410 | * Validates and prepares the data. | |
411 | * | |
412 | * @return bool false is any error occured. | |
413 | */ | |
414 | public function prepare() { | |
0e8f5610 | 415 | global $DB, $SITE; |
9a7cd639 FM |
416 | $this->prepared = true; |
417 | ||
418 | // Validate the shortname. | |
419 | if (!empty($this->shortname) || is_numeric($this->shortname)) { | |
420 | if ($this->shortname !== clean_param($this->shortname, PARAM_TEXT)) { | |
1d1898ac | 421 | $this->error('invalidshortname', new lang_string('invalidshortname', 'tool_uploadcourse')); |
9a7cd639 FM |
422 | return false; |
423 | } | |
714343c0 PH |
424 | |
425 | // Ensure we don't overflow the maximum length of the shortname field. | |
426 | if (core_text::strlen($this->shortname) > 255) { | |
427 | $this->error('invalidshortnametoolong', new lang_string('invalidshortnametoolong', 'tool_uploadcourse', 255)); | |
428 | return false; | |
429 | } | |
9a7cd639 FM |
430 | } |
431 | ||
432 | $exists = $this->exists(); | |
433 | ||
434 | // Do we want to delete the course? | |
435 | if ($this->options['delete']) { | |
436 | if (!$exists) { | |
1d1898ac | 437 | $this->error('cannotdeletecoursenotexist', new lang_string('cannotdeletecoursenotexist', 'tool_uploadcourse')); |
9a7cd639 FM |
438 | return false; |
439 | } else if (!$this->can_delete()) { | |
1d1898ac | 440 | $this->error('coursedeletionnotallowed', new lang_string('coursedeletionnotallowed', 'tool_uploadcourse')); |
9a7cd639 FM |
441 | return false; |
442 | } | |
443 | ||
96aaf654 | 444 | $this->do = self::DO_DELETE; |
9a7cd639 FM |
445 | return true; |
446 | } | |
447 | ||
448 | // Can we create/update the course under those conditions? | |
449 | if ($exists) { | |
450 | if ($this->mode === tool_uploadcourse_processor::MODE_CREATE_NEW) { | |
3f57c87e FM |
451 | $this->error('courseexistsanduploadnotallowed', |
452 | new lang_string('courseexistsanduploadnotallowed', 'tool_uploadcourse')); | |
9a7cd639 | 453 | return false; |
0e8f5610 FM |
454 | } else if ($this->can_update()) { |
455 | // We can never allow for any front page changes! | |
456 | if ($this->shortname == $SITE->shortname) { | |
457 | $this->error('cannotupdatefrontpage', new lang_string('cannotupdatefrontpage', 'tool_uploadcourse')); | |
458 | return false; | |
459 | } | |
9a7cd639 FM |
460 | } |
461 | } else { | |
462 | if (!$this->can_create()) { | |
1d1898ac FM |
463 | $this->error('coursedoesnotexistandcreatenotallowed', |
464 | new lang_string('coursedoesnotexistandcreatenotallowed', 'tool_uploadcourse')); | |
9a7cd639 FM |
465 | return false; |
466 | } | |
467 | } | |
468 | ||
469 | // Basic data. | |
470 | $coursedata = array(); | |
471 | foreach ($this->rawdata as $field => $value) { | |
472 | if (!in_array($field, self::$validfields)) { | |
473 | continue; | |
474 | } else if ($field == 'shortname') { | |
475 | // Let's leave it apart from now, use $this->shortname only. | |
476 | continue; | |
477 | } | |
478 | $coursedata[$field] = $value; | |
479 | } | |
480 | ||
481 | $mode = $this->mode; | |
482 | $updatemode = $this->updatemode; | |
483 | $usedefaults = $this->can_use_defaults(); | |
484 | ||
8383903f FM |
485 | // Resolve the category, and fail if not found. |
486 | $errors = array(); | |
487 | $catid = tool_uploadcourse_helper::resolve_category($this->rawdata, $errors); | |
488 | if (empty($errors)) { | |
489 | $coursedata['category'] = $catid; | |
490 | } else { | |
491 | foreach ($errors as $key => $message) { | |
492 | $this->error($key, $message); | |
493 | } | |
494 | return false; | |
495 | } | |
496 | ||
714343c0 PH |
497 | // Ensure we don't overflow the maximum length of the fullname field. |
498 | if (!empty($coursedata['fullname']) && core_text::strlen($coursedata['fullname']) > 254) { | |
499 | $this->error('invalidfullnametoolong', new lang_string('invalidfullnametoolong', 'tool_uploadcourse', 254)); | |
500 | return false; | |
501 | } | |
502 | ||
9a7cd639 FM |
503 | // If the course does not exist, or will be forced created. |
504 | if (!$exists || $mode === tool_uploadcourse_processor::MODE_CREATE_ALL) { | |
505 | ||
506 | // Mandatory fields upon creation. | |
1d1898ac | 507 | $errors = array(); |
9a7cd639 FM |
508 | foreach (self::$mandatoryfields as $field) { |
509 | if ((!isset($coursedata[$field]) || $coursedata[$field] === '') && | |
510 | (!isset($this->defaults[$field]) || $this->defaults[$field] === '')) { | |
1d1898ac | 511 | $errors[] = $field; |
9a7cd639 FM |
512 | } |
513 | } | |
1d1898ac FM |
514 | if (!empty($errors)) { |
515 | $this->error('missingmandatoryfields', new lang_string('missingmandatoryfields', 'tool_uploadcourse', | |
516 | implode(', ', $errors))); | |
9a7cd639 FM |
517 | return false; |
518 | } | |
519 | } | |
520 | ||
521 | // Should the course be renamed? | |
522 | if (!empty($this->options['rename']) || is_numeric($this->options['rename'])) { | |
1d1898ac FM |
523 | if (!$this->can_update()) { |
524 | $this->error('canonlyrenameinupdatemode', new lang_string('canonlyrenameinupdatemode', 'tool_uploadcourse')); | |
525 | return false; | |
526 | } else if (!$exists) { | |
527 | $this->error('cannotrenamecoursenotexist', new lang_string('cannotrenamecoursenotexist', 'tool_uploadcourse')); | |
9a7cd639 FM |
528 | return false; |
529 | } else if (!$this->can_rename()) { | |
1d1898ac | 530 | $this->error('courserenamingnotallowed', new lang_string('courserenamingnotallowed', 'tool_uploadcourse')); |
9a7cd639 FM |
531 | return false; |
532 | } else if ($this->options['rename'] !== clean_param($this->options['rename'], PARAM_TEXT)) { | |
1d1898ac | 533 | $this->error('invalidshortname', new lang_string('invalidshortname', 'tool_uploadcourse')); |
9a7cd639 FM |
534 | return false; |
535 | } else if ($this->exists($this->options['rename'])) { | |
1d1898ac FM |
536 | $this->error('cannotrenameshortnamealreadyinuse', |
537 | new lang_string('cannotrenameshortnamealreadyinuse', 'tool_uploadcourse')); | |
9a7cd639 FM |
538 | return false; |
539 | } else if (isset($coursedata['idnumber']) && | |
540 | $DB->count_records_select('course', 'idnumber = :idn AND shortname != :sn', | |
541 | array('idn' => $coursedata['idnumber'], 'sn' => $this->shortname)) > 0) { | |
1d1898ac | 542 | $this->error('cannotrenameidnumberconflict', new lang_string('cannotrenameidnumberconflict', 'tool_uploadcourse')); |
9a7cd639 FM |
543 | return false; |
544 | } | |
545 | $coursedata['shortname'] = $this->options['rename']; | |
1d1898ac FM |
546 | $this->status('courserenamed', new lang_string('courserenamed', 'tool_uploadcourse', |
547 | array('from' => $this->shortname, 'to' => $coursedata['shortname']))); | |
9a7cd639 FM |
548 | } |
549 | ||
550 | // Should we generate a shortname? | |
551 | if (empty($this->shortname) && !is_numeric($this->shortname)) { | |
552 | if (empty($this->importoptions['shortnametemplate'])) { | |
1d1898ac | 553 | $this->error('missingshortnamenotemplate', new lang_string('missingshortnamenotemplate', 'tool_uploadcourse')); |
9a7cd639 FM |
554 | return false; |
555 | } else if (!$this->can_only_create()) { | |
1d1898ac FM |
556 | $this->error('cannotgenerateshortnameupdatemode', |
557 | new lang_string('cannotgenerateshortnameupdatemode', 'tool_uploadcourse')); | |
9a7cd639 FM |
558 | return false; |
559 | } else { | |
3f57c87e FM |
560 | $newshortname = tool_uploadcourse_helper::generate_shortname($coursedata, |
561 | $this->importoptions['shortnametemplate']); | |
9a7cd639 | 562 | if (is_null($newshortname)) { |
1d1898ac | 563 | $this->error('generatedshortnameinvalid', new lang_string('generatedshortnameinvalid', 'tool_uploadcourse')); |
9a7cd639 FM |
564 | return false; |
565 | } else if ($this->exists($newshortname)) { | |
566 | if ($mode === tool_uploadcourse_processor::MODE_CREATE_NEW) { | |
1d1898ac FM |
567 | $this->error('generatedshortnamealreadyinuse', |
568 | new lang_string('generatedshortnamealreadyinuse', 'tool_uploadcourse')); | |
9a7cd639 FM |
569 | return false; |
570 | } | |
571 | $exists = true; | |
572 | } | |
5b2f7718 FM |
573 | $this->status('courseshortnamegenerated', new lang_string('courseshortnamegenerated', 'tool_uploadcourse', |
574 | $newshortname)); | |
9a7cd639 FM |
575 | $this->shortname = $newshortname; |
576 | } | |
577 | } | |
578 | ||
579 | // If exists, but we only want to create courses, increment the shortname. | |
580 | if ($exists && $mode === tool_uploadcourse_processor::MODE_CREATE_ALL) { | |
581 | $original = $this->shortname; | |
582 | $this->shortname = tool_uploadcourse_helper::increment_shortname($this->shortname); | |
583 | $exists = false; | |
584 | if ($this->shortname != $original) { | |
1d1898ac FM |
585 | $this->status('courseshortnameincremented', new lang_string('courseshortnameincremented', 'tool_uploadcourse', |
586 | array('from' => $original, 'to' => $this->shortname))); | |
9a7cd639 FM |
587 | if (isset($coursedata['idnumber'])) { |
588 | $originalidn = $coursedata['idnumber']; | |
8383903f | 589 | $coursedata['idnumber'] = tool_uploadcourse_helper::increment_idnumber($coursedata['idnumber']); |
9a7cd639 | 590 | if ($originalidn != $coursedata['idnumber']) { |
1d1898ac FM |
591 | $this->status('courseidnumberincremented', new lang_string('courseidnumberincremented', 'tool_uploadcourse', |
592 | array('from' => $originalidn, 'to' => $coursedata['idnumber']))); | |
9a7cd639 FM |
593 | } |
594 | } | |
595 | } | |
596 | } | |
597 | ||
f54c49cc FM |
598 | // If the course does not exist, ensure that the ID number is not taken. |
599 | if (!$exists && isset($coursedata['idnumber'])) { | |
600 | if ($DB->count_records_select('course', 'idnumber = :idn', array('idn' => $coursedata['idnumber'])) > 0) { | |
601 | $this->error('idnumberalreadyinuse', new lang_string('idnumberalreadyinuse', 'tool_uploadcourse')); | |
602 | return false; | |
603 | } | |
604 | } | |
605 | ||
f258a958 DM |
606 | // Course start date. |
607 | if (!empty($coursedata['startdate'])) { | |
608 | $coursedata['startdate'] = strtotime($coursedata['startdate']); | |
609 | } | |
610 | ||
fbcdb0d7 DNA |
611 | // Course end date. |
612 | if (!empty($coursedata['enddate'])) { | |
613 | $coursedata['enddate'] = strtotime($coursedata['enddate']); | |
614 | } | |
615 | ||
1433a078 DS |
616 | // If lang is specified, check the user is allowed to set that field. |
617 | if (!empty($coursedata['lang'])) { | |
618 | if ($exists) { | |
619 | $courseid = $DB->get_field('course', 'id', ['shortname' => $this->shortname]); | |
620 | if (!has_capability('moodle/course:setforcedlanguage', context_course::instance($courseid))) { | |
621 | $this->error('cannotforcelang', new lang_string('cannotforcelang', 'tool_uploadcourse')); | |
622 | return false; | |
623 | } | |
624 | } else { | |
625 | $catcontext = context_coursecat::instance($coursedata['category']); | |
626 | if (!guess_if_creator_will_have_course_capability('moodle/course:setforcedlanguage', $catcontext)) { | |
627 | $this->error('cannotforcelang', new lang_string('cannotforcelang', 'tool_uploadcourse')); | |
628 | return false; | |
629 | } | |
630 | } | |
631 | } | |
632 | ||
9a7cd639 FM |
633 | // Ultimate check mode vs. existence. |
634 | switch ($mode) { | |
635 | case tool_uploadcourse_processor::MODE_CREATE_NEW: | |
636 | case tool_uploadcourse_processor::MODE_CREATE_ALL: | |
637 | if ($exists) { | |
1d1898ac FM |
638 | $this->error('courseexistsanduploadnotallowed', |
639 | new lang_string('courseexistsanduploadnotallowed', 'tool_uploadcourse')); | |
9a7cd639 FM |
640 | return false; |
641 | } | |
642 | break; | |
643 | case tool_uploadcourse_processor::MODE_UPDATE_ONLY: | |
644 | if (!$exists) { | |
1d1898ac FM |
645 | $this->error('coursedoesnotexistandcreatenotallowed', |
646 | new lang_string('coursedoesnotexistandcreatenotallowed', 'tool_uploadcourse')); | |
9a7cd639 FM |
647 | return false; |
648 | } | |
649 | // No break! | |
650 | case tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE: | |
651 | if ($exists) { | |
652 | if ($updatemode === tool_uploadcourse_processor::UPDATE_NOTHING) { | |
1d1898ac FM |
653 | $this->error('updatemodedoessettonothing', |
654 | new lang_string('updatemodedoessettonothing', 'tool_uploadcourse')); | |
9a7cd639 FM |
655 | return false; |
656 | } | |
657 | } | |
658 | break; | |
659 | default: | |
660 | // O_o Huh?! This should really never happen here! | |
1d1898ac | 661 | $this->error('unknownimportmode', new lang_string('unknownimportmode', 'tool_uploadcourse')); |
9a7cd639 FM |
662 | return false; |
663 | } | |
664 | ||
9a7cd639 FM |
665 | // Get final data. |
666 | if ($exists) { | |
667 | $missingonly = ($updatemode === tool_uploadcourse_processor::UPDATE_MISSING_WITH_DATA_OR_DEFAUTLS); | |
668 | $coursedata = $this->get_final_update_data($coursedata, $usedefaults, $missingonly); | |
0e8f5610 FM |
669 | |
670 | // Make sure we are not trying to mess with the front page, though we should never get here! | |
671 | if ($coursedata['id'] == $SITE->id) { | |
672 | $this->error('cannotupdatefrontpage', new lang_string('cannotupdatefrontpage', 'tool_uploadcourse')); | |
673 | return false; | |
674 | } | |
675 | ||
96aaf654 | 676 | $this->do = self::DO_UPDATE; |
9a7cd639 FM |
677 | } else { |
678 | $coursedata = $this->get_final_create_data($coursedata); | |
96aaf654 | 679 | $this->do = self::DO_CREATE; |
9a7cd639 FM |
680 | } |
681 | ||
8643c576 DM |
682 | // Validate course start and end dates. |
683 | if ($exists) { | |
684 | // We also check existing start and end dates if we are updating an existing course. | |
685 | $existingdata = $DB->get_record('course', array('shortname' => $this->shortname)); | |
686 | if (empty($coursedata['startdate'])) { | |
687 | $coursedata['startdate'] = $existingdata->startdate; | |
688 | } | |
689 | if (empty($coursedata['enddate'])) { | |
690 | $coursedata['enddate'] = $existingdata->enddate; | |
691 | } | |
692 | } | |
693 | if ($errorcode = course_validate_dates($coursedata)) { | |
694 | $this->error($errorcode, new lang_string($errorcode, 'error')); | |
695 | return false; | |
696 | } | |
697 | ||
9a7cd639 | 698 | // Add role renaming. |
1d1898ac FM |
699 | $errors = array(); |
700 | $rolenames = tool_uploadcourse_helper::get_role_names($this->rawdata, $errors); | |
701 | if (!empty($errors)) { | |
702 | foreach ($errors as $key => $message) { | |
703 | $this->error($key, $message); | |
704 | } | |
705 | return false; | |
706 | } | |
707 | foreach ($rolenames as $rolekey => $rolename) { | |
9a7cd639 FM |
708 | $coursedata[$rolekey] = $rolename; |
709 | } | |
710 | ||
d62fc08e PH |
711 | // Custom fields. If the course already exists and mode isn't set to force creation, we can use its context. |
712 | if ($exists && $mode !== tool_uploadcourse_processor::MODE_CREATE_ALL) { | |
713 | $context = context_course::instance($coursedata['id']); | |
714 | } else { | |
715 | // The category ID is taken from the defaults if it exists, otherwise from course data. | |
716 | $context = context_coursecat::instance($this->defaults['category'] ?? $coursedata['category']); | |
717 | } | |
718 | $customfielddata = tool_uploadcourse_helper::get_custom_course_field_data($this->rawdata, $this->defaults, $context, | |
719 | $errors); | |
720 | if (!empty($errors)) { | |
721 | foreach ($errors as $key => $message) { | |
722 | $this->error($key, $message); | |
723 | } | |
724 | ||
725 | return false; | |
726 | } | |
727 | ||
728 | foreach ($customfielddata as $name => $value) { | |
729 | $coursedata[$name] = $value; | |
730 | } | |
731 | ||
1d1898ac | 732 | // Some validation. |
9a7cd639 | 733 | if (!empty($coursedata['format']) && !in_array($coursedata['format'], tool_uploadcourse_helper::get_course_formats())) { |
1d1898ac FM |
734 | $this->error('invalidcourseformat', new lang_string('invalidcourseformat', 'tool_uploadcourse')); |
735 | return false; | |
9a7cd639 FM |
736 | } |
737 | ||
a589b992 MG |
738 | // Add data for course format options. |
739 | if (isset($coursedata['format']) || $exists) { | |
740 | if (isset($coursedata['format'])) { | |
741 | $courseformat = course_get_format((object)['format' => $coursedata['format']]); | |
742 | } else { | |
743 | $courseformat = course_get_format($existingdata); | |
744 | } | |
745 | $coursedata += $courseformat->validate_course_format_options($this->rawdata); | |
746 | } | |
b1ed151f | 747 | |
d4beb211 LB |
748 | // Special case, 'numsections' is not a course format option any more but still should apply from the template course, |
749 | // if any, and otherwise from defaults. | |
b1ed151f MG |
750 | if (!$exists || !array_key_exists('numsections', $coursedata)) { |
751 | if (isset($this->rawdata['numsections']) && is_numeric($this->rawdata['numsections'])) { | |
752 | $coursedata['numsections'] = (int)$this->rawdata['numsections']; | |
d4beb211 LB |
753 | } else if (isset($this->options['templatecourse'])) { |
754 | $numsections = tool_uploadcourse_helper::get_coursesection_count($this->options['templatecourse']); | |
755 | if ($numsections != 0) { | |
756 | $coursedata['numsections'] = $numsections; | |
757 | } else { | |
758 | $coursedata['numsections'] = get_config('moodlecourse', 'numsections'); | |
759 | } | |
b1ed151f MG |
760 | } else { |
761 | $coursedata['numsections'] = get_config('moodlecourse', 'numsections'); | |
762 | } | |
763 | } | |
764 | ||
2e7f4fce LB |
765 | // Visibility can only be 0 or 1. |
766 | if (!empty($coursedata['visible']) AND !($coursedata['visible'] == 0 OR $coursedata['visible'] == 1)) { | |
767 | $this->error('invalidvisibilitymode', new lang_string('invalidvisibilitymode', 'tool_uploadcourse')); | |
768 | return false; | |
769 | } | |
770 | ||
9a7cd639 FM |
771 | // Saving data. |
772 | $this->data = $coursedata; | |
b9a09480 PH |
773 | |
774 | // Get enrolment data. Where the course already exists, we can also perform validation. | |
853b6cff | 775 | $this->enrolmentdata = tool_uploadcourse_helper::get_enrolment_data($this->rawdata); |
b9a09480 PH |
776 | if ($exists) { |
777 | $errors = $this->validate_enrolment_data($coursedata['id'], $this->enrolmentdata); | |
778 | ||
779 | if (!empty($errors)) { | |
780 | foreach ($errors as $key => $message) { | |
781 | $this->error($key, $message); | |
782 | } | |
783 | ||
784 | return false; | |
785 | } | |
786 | } | |
9a7cd639 | 787 | |
54f63cd1 MG |
788 | if (isset($this->rawdata['tags']) && strval($this->rawdata['tags']) !== '') { |
789 | $this->data['tags'] = preg_split('/\s*,\s*/', trim($this->rawdata['tags']), -1, PREG_SPLIT_NO_EMPTY); | |
790 | } | |
791 | ||
9a7cd639 | 792 | // Restore data. |
7bfc9edd FM |
793 | // TODO Speed up things by not really extracting the backup just yet, but checking that |
794 | // the backup file or shortname passed are valid. Extraction should happen in proceed(). | |
9a7cd639 | 795 | $this->restoredata = $this->get_restore_content_dir(); |
7bfc9edd FM |
796 | if ($this->restoredata === false) { |
797 | return false; | |
798 | } | |
9a7cd639 | 799 | |
3f57c87e | 800 | // We can only reset courses when allowed and we are updating the course. |
9a7cd639 | 801 | if ($this->importoptions['reset'] || $this->options['reset']) { |
96aaf654 | 802 | if ($this->do !== self::DO_UPDATE) { |
1d1898ac FM |
803 | $this->error('canonlyresetcourseinupdatemode', |
804 | new lang_string('canonlyresetcourseinupdatemode', 'tool_uploadcourse')); | |
9a7cd639 FM |
805 | return false; |
806 | } else if (!$this->can_reset()) { | |
1d1898ac | 807 | $this->error('courseresetnotallowed', new lang_string('courseresetnotallowed', 'tool_uploadcourse')); |
9a7cd639 FM |
808 | return false; |
809 | } | |
810 | } | |
811 | ||
812 | return true; | |
813 | } | |
814 | ||
815 | /** | |
816 | * Proceed with the import of the course. | |
817 | * | |
818 | * @return void | |
819 | */ | |
820 | public function proceed() { | |
821 | global $CFG, $USER; | |
822 | ||
823 | if (!$this->prepared) { | |
824 | throw new coding_exception('The course has not been prepared.'); | |
825 | } else if ($this->has_errors()) { | |
826 | throw new moodle_exception('Cannot proceed, errors were detected.'); | |
1e3e4efd FM |
827 | } else if ($this->processstarted) { |
828 | throw new coding_exception('The process has already been started.'); | |
9a7cd639 | 829 | } |
1e3e4efd | 830 | $this->processstarted = true; |
9a7cd639 | 831 | |
96aaf654 | 832 | if ($this->do === self::DO_DELETE) { |
1d1898ac FM |
833 | if ($this->delete()) { |
834 | $this->status('coursedeleted', new lang_string('coursedeleted', 'tool_uploadcourse')); | |
835 | } else { | |
836 | $this->error('errorwhiledeletingcourse', new lang_string('errorwhiledeletingcourse', 'tool_uploadcourse')); | |
837 | } | |
8383903f | 838 | return true; |
96aaf654 | 839 | } else if ($this->do === self::DO_CREATE) { |
9a7cd639 | 840 | $course = create_course((object) $this->data); |
1e3e4efd | 841 | $this->id = $course->id; |
1d1898ac | 842 | $this->status('coursecreated', new lang_string('coursecreated', 'tool_uploadcourse')); |
96aaf654 | 843 | } else if ($this->do === self::DO_UPDATE) { |
9a7cd639 FM |
844 | $course = (object) $this->data; |
845 | update_course($course); | |
1e3e4efd | 846 | $this->id = $course->id; |
1d1898ac | 847 | $this->status('courseupdated', new lang_string('courseupdated', 'tool_uploadcourse')); |
9a7cd639 FM |
848 | } else { |
849 | // Strangely the outcome has not been defined, or is unknown! | |
850 | throw new coding_exception('Unknown outcome!'); | |
851 | } | |
852 | ||
853 | // Restore a course. | |
854 | if (!empty($this->restoredata)) { | |
855 | $rc = new restore_controller($this->restoredata, $course->id, backup::INTERACTIVE_NO, | |
856 | backup::MODE_IMPORT, $USER->id, backup::TARGET_CURRENT_ADDING); | |
857 | ||
858 | // Check if the format conversion must happen first. | |
859 | if ($rc->get_status() == backup::STATUS_REQUIRE_CONV) { | |
860 | $rc->convert(); | |
861 | } | |
862 | if ($rc->execute_precheck()) { | |
863 | $rc->execute_plan(); | |
1d1898ac | 864 | $this->status('courserestored', new lang_string('courserestored', 'tool_uploadcourse')); |
9a7cd639 | 865 | } else { |
1d1898ac | 866 | $this->error('errorwhilerestoringcourse', new lang_string('errorwhilerestoringthecourse', 'tool_uploadcourse')); |
9a7cd639 FM |
867 | } |
868 | $rc->destroy(); | |
869 | } | |
870 | ||
871 | // Proceed with enrolment data. | |
872 | $this->process_enrolment_data($course); | |
873 | ||
874 | // Reset the course. | |
875 | if ($this->importoptions['reset'] || $this->options['reset']) { | |
96aaf654 | 876 | if ($this->do === self::DO_UPDATE && $this->can_reset()) { |
9a7cd639 | 877 | $this->reset($course); |
1d1898ac | 878 | $this->status('coursereset', new lang_string('coursereset', 'tool_uploadcourse')); |
9a7cd639 FM |
879 | } |
880 | } | |
881 | ||
882 | // Mark context as dirty. | |
883 | $context = context_course::instance($course->id); | |
884 | $context->mark_dirty(); | |
885 | } | |
886 | ||
b9a09480 PH |
887 | /** |
888 | * Validate passed enrolment data against an existing course | |
889 | * | |
890 | * @param int $courseid | |
891 | * @param array[] $enrolmentdata | |
892 | * @return lang_string[] Errors keyed on error code | |
893 | */ | |
894 | protected function validate_enrolment_data(int $courseid, array $enrolmentdata): array { | |
895 | // Nothing to validate. | |
896 | if (empty($enrolmentdata)) { | |
897 | return []; | |
898 | } | |
899 | ||
900 | $errors = []; | |
901 | ||
902 | $enrolmentplugins = tool_uploadcourse_helper::get_enrolment_plugins(); | |
903 | $instances = enrol_get_instances($courseid, false); | |
904 | ||
905 | foreach ($enrolmentdata as $method => $options) { | |
906 | $plugin = $enrolmentplugins[$method]; | |
907 | ||
908 | // Find matching instances by enrolment method. | |
909 | $methodinstances = array_filter($instances, static function(stdClass $instance) use ($method) { | |
910 | return (strcmp($instance->enrol, $method) == 0); | |
911 | }); | |
912 | ||
913 | if (!empty($options['delete'])) { | |
914 | // Ensure user is able to delete the instances. | |
915 | foreach ($methodinstances as $methodinstance) { | |
916 | if (!$plugin->can_delete_instance($methodinstance)) { | |
917 | $errors['errorcannotdeleteenrolment'] = new lang_string('errorcannotdeleteenrolment', 'tool_uploadcourse', | |
918 | $plugin->get_instance_name($methodinstance)); | |
919 | ||
920 | break; | |
921 | } | |
922 | } | |
923 | } else if (!empty($options['disable'])) { | |
924 | // Ensure user is able to toggle instance statuses. | |
925 | foreach ($methodinstances as $methodinstance) { | |
926 | if (!$plugin->can_hide_show_instance($methodinstance)) { | |
927 | $errors['errorcannotdisableenrolment'] = | |
928 | new lang_string('errorcannotdisableenrolment', 'tool_uploadcourse', | |
929 | $plugin->get_instance_name($methodinstance)); | |
930 | ||
931 | break; | |
932 | } | |
933 | } | |
934 | } else { | |
935 | // Ensure user is able to create/update instance. | |
936 | $methodinstance = empty($methodinstances) ? null : reset($methodinstances); | |
937 | if ((empty($methodinstance) && !$plugin->can_add_instance($courseid)) || | |
938 | (!empty($methodinstance) && !$plugin->can_edit_instance($methodinstance))) { | |
939 | ||
940 | $errors['errorcannotcreateorupdateenrolment'] = | |
941 | new lang_string('errorcannotcreateorupdateenrolment', 'tool_uploadcourse', | |
942 | $plugin->get_instance_name($methodinstance)); | |
943 | ||
944 | break; | |
945 | } | |
946 | } | |
947 | } | |
948 | ||
949 | return $errors; | |
950 | } | |
951 | ||
9a7cd639 FM |
952 | /** |
953 | * Add the enrolment data for the course. | |
954 | * | |
955 | * @param object $course course record. | |
956 | * @return void | |
957 | */ | |
958 | protected function process_enrolment_data($course) { | |
959 | global $DB; | |
960 | ||
961 | $enrolmentdata = $this->enrolmentdata; | |
962 | if (empty($enrolmentdata)) { | |
963 | return; | |
964 | } | |
965 | ||
966 | $enrolmentplugins = tool_uploadcourse_helper::get_enrolment_plugins(); | |
967 | $instances = enrol_get_instances($course->id, false); | |
968 | foreach ($enrolmentdata as $enrolmethod => $method) { | |
969 | ||
970 | $instance = null; | |
971 | foreach ($instances as $i) { | |
972 | if ($i->enrol == $enrolmethod) { | |
973 | $instance = $i; | |
974 | break; | |
975 | } | |
976 | } | |
977 | ||
978 | $todelete = isset($method['delete']) && $method['delete']; | |
979 | $todisable = isset($method['disable']) && $method['disable']; | |
980 | unset($method['delete']); | |
981 | unset($method['disable']); | |
982 | ||
b8e1eec4 | 983 | if ($todelete) { |
9a7cd639 | 984 | // Remove the enrolment method. |
b8e1eec4 FR |
985 | if ($instance) { |
986 | $plugin = $enrolmentplugins[$instance->enrol]; | |
987 | ||
988 | // Ensure user is able to delete the instance. | |
989 | if ($plugin->can_delete_instance($instance)) { | |
990 | $plugin->delete_instance($instance); | |
991 | } else { | |
992 | $this->error('errorcannotdeleteenrolment', | |
993 | new lang_string('errorcannotdeleteenrolment', 'tool_uploadcourse', | |
994 | $plugin->get_instance_name($instance))); | |
9a7cd639 FM |
995 | } |
996 | } | |
997 | } else { | |
b9a09480 PH |
998 | // Create/update enrolment. |
999 | $plugin = $enrolmentplugins[$enrolmethod]; | |
1000 | ||
b8e1eec4 FR |
1001 | $status = ($todisable) ? ENROL_INSTANCE_DISABLED : ENROL_INSTANCE_ENABLED; |
1002 | ||
1003 | // Create a new instance if necessary. | |
b9a09480 | 1004 | if (empty($instance) && $plugin->can_add_instance($course->id)) { |
b8e1eec4 FR |
1005 | $instanceid = $plugin->add_default_instance($course); |
1006 | $instance = $DB->get_record('enrol', ['id' => $instanceid]); | |
9a7cd639 | 1007 | $instance->roleid = $plugin->get_config('roleid'); |
b8e1eec4 FR |
1008 | // On creation the user can decide the status. |
1009 | $plugin->update_status($instance, $status); | |
1010 | } | |
1011 | ||
1012 | // Check if the we need to update the instance status. | |
1013 | if ($instance && $status != $instance->status) { | |
1014 | if ($plugin->can_hide_show_instance($instance)) { | |
1015 | $plugin->update_status($instance, $status); | |
1016 | } else { | |
1017 | $this->error('errorcannotdisableenrolment', | |
1018 | new lang_string('errorcannotdisableenrolment', 'tool_uploadcourse', | |
1019 | $plugin->get_instance_name($instance))); | |
1020 | break; | |
1021 | } | |
1022 | } | |
1023 | ||
1024 | if (empty($instance) || !$plugin->can_edit_instance($instance)) { | |
b9a09480 PH |
1025 | $this->error('errorcannotcreateorupdateenrolment', |
1026 | new lang_string('errorcannotcreateorupdateenrolment', 'tool_uploadcourse', | |
1027 | $plugin->get_instance_name($instance))); | |
1028 | ||
1029 | break; | |
9a7cd639 FM |
1030 | } |
1031 | ||
1032 | // Now update values. | |
1033 | foreach ($method as $k => $v) { | |
1034 | $instance->{$k} = $v; | |
1035 | } | |
1036 | ||
1037 | // Sort out the start, end and date. | |
853b6cff DP |
1038 | $instance->enrolstartdate = (isset($method['startdate']) ? strtotime($method['startdate']) : 0); |
1039 | $instance->enrolenddate = (isset($method['enddate']) ? strtotime($method['enddate']) : 0); | |
9a7cd639 FM |
1040 | |
1041 | // Is the enrolment period set? | |
853b6cff DP |
1042 | if (isset($method['enrolperiod']) && ! empty($method['enrolperiod'])) { |
1043 | if (preg_match('/^\d+$/', $method['enrolperiod'])) { | |
9a7cd639 FM |
1044 | $method['enrolperiod'] = (int) $method['enrolperiod']; |
1045 | } else { | |
1046 | // Try and convert period to seconds. | |
1047 | $method['enrolperiod'] = strtotime('1970-01-01 GMT + ' . $method['enrolperiod']); | |
1048 | } | |
1049 | $instance->enrolperiod = $method['enrolperiod']; | |
1050 | } | |
1051 | if ($instance->enrolstartdate > 0 && isset($method['enrolperiod'])) { | |
1052 | $instance->enrolenddate = $instance->enrolstartdate + $method['enrolperiod']; | |
1053 | } | |
1054 | if ($instance->enrolenddate > 0) { | |
1055 | $instance->enrolperiod = $instance->enrolenddate - $instance->enrolstartdate; | |
1056 | } | |
1057 | if ($instance->enrolenddate < $instance->enrolstartdate) { | |
1058 | $instance->enrolenddate = $instance->enrolstartdate; | |
1059 | } | |
1060 | ||
1061 | // Sort out the given role. This does not filter the roles allowed in the course. | |
1062 | if (isset($method['role'])) { | |
1063 | $roleids = tool_uploadcourse_helper::get_role_ids(); | |
1064 | if (isset($roleids[$method['role']])) { | |
1065 | $instance->roleid = $roleids[$method['role']]; | |
1066 | } | |
1067 | } | |
1068 | ||
1069 | $instance->timemodified = time(); | |
1070 | $DB->update_record('enrol', $instance); | |
1071 | } | |
1072 | } | |
1073 | } | |
1074 | ||
1075 | /** | |
1076 | * Reset the current course. | |
1077 | * | |
1078 | * This does not reset any of the content of the activities. | |
1079 | * | |
3f57c87e | 1080 | * @param stdClass $course the course object of the course to reset. |
9a7cd639 FM |
1081 | * @return array status array of array component, item, error. |
1082 | */ | |
1083 | protected function reset($course) { | |
1084 | global $DB; | |
1085 | ||
1086 | $resetdata = new stdClass(); | |
1087 | $resetdata->id = $course->id; | |
1088 | $resetdata->reset_start_date = time(); | |
9a7cd639 FM |
1089 | $resetdata->reset_events = true; |
1090 | $resetdata->reset_notes = true; | |
1091 | $resetdata->delete_blog_associations = true; | |
1092 | $resetdata->reset_completion = true; | |
1093 | $resetdata->reset_roles_overrides = true; | |
1094 | $resetdata->reset_roles_local = true; | |
1095 | $resetdata->reset_groups_members = true; | |
1096 | $resetdata->reset_groups_remove = true; | |
1097 | $resetdata->reset_groupings_members = true; | |
1098 | $resetdata->reset_groupings_remove = true; | |
1099 | $resetdata->reset_gradebook_items = true; | |
1100 | $resetdata->reset_gradebook_grades = true; | |
1101 | $resetdata->reset_comments = true; | |
1102 | ||
1103 | if (empty($course->startdate)) { | |
1104 | $course->startdate = $DB->get_field_select('course', 'startdate', 'id = :id', array('id' => $course->id)); | |
1105 | } | |
1106 | $resetdata->reset_start_date_old = $course->startdate; | |
1107 | ||
fbcdb0d7 DNA |
1108 | if (empty($course->enddate)) { |
1109 | $course->enddate = $DB->get_field_select('course', 'enddate', 'id = :id', array('id' => $course->id)); | |
1110 | } | |
8643c576 | 1111 | $resetdata->reset_end_date_old = $course->enddate; |
fbcdb0d7 | 1112 | |
9a7cd639 FM |
1113 | // Add roles. |
1114 | $roles = tool_uploadcourse_helper::get_role_ids(); | |
1115 | $resetdata->unenrol_users = array_values($roles); | |
1116 | $resetdata->unenrol_users[] = 0; // Enrolled without role. | |
1117 | ||
1118 | return reset_course_userdata($resetdata); | |
1119 | } | |
1120 | ||
1d1898ac FM |
1121 | /** |
1122 | * Log a status | |
1123 | * | |
1124 | * @param string $code status code. | |
1125 | * @param lang_string $message status message. | |
1126 | * @return void | |
1127 | */ | |
1128 | protected function status($code, lang_string $message) { | |
1129 | if (array_key_exists($code, $this->statuses)) { | |
1130 | throw new coding_exception('Status code already defined'); | |
1131 | } | |
1132 | $this->statuses[$code] = $message; | |
1133 | } | |
1134 | ||
9a7cd639 | 1135 | } |