diff --git a/admin/tool/uploadcourse/classes/course.php b/admin/tool/uploadcourse/classes/course.php index b282bc98884..fc6bd0960d0 100644 --- a/admin/tool/uploadcourse/classes/course.php +++ b/admin/tool/uploadcourse/classes/course.php @@ -52,7 +52,7 @@ class tool_uploadcourse_course { protected $enrolmentdata; /** @var array errors. */ - protected $errors; + protected $errors = array(); /** @var array containing options passed from the processor. */ protected $importoptions = array(); @@ -78,6 +78,9 @@ class tool_uploadcourse_course { /** @var string course shortname. */ protected $shortname; + /** @var array errors. */ + protected $statuses = array(); + /** @var int update mode. Matches tool_uploadcourse_processor::UPDATE_* */ protected $updatemode; @@ -223,11 +226,15 @@ class tool_uploadcourse_course { /** * Log an error * - * @param string $message error message. + * @param string $code error code. + * @param lang_string $message error message. * @return void */ - protected function error($message) { - $this->errors[] = $message; + protected function error($code, lang_string $message) { + if (array_key_exists($code, $this->errors)) { + throw new coding_exception('Error code already defined'); + } + $this->errors[$code] = $message; } /** @@ -315,7 +322,7 @@ class tool_uploadcourse_course { /** * Get the directory of the object to restore. * - * @return string subdirectory in $CFG->tempdir/backup/... + * @return string|false subdirectory in $CFG->tempdir/backup/... */ protected function get_restore_content_dir() { $backupfile = null; @@ -327,7 +334,15 @@ class tool_uploadcourse_course { $shortname = $this->options['templatecourse']; } - $dir = tool_uploadcourse_helper::get_restore_content_dir($backupfile, $shortname); + $errors = array(); + $dir = tool_uploadcourse_helper::get_restore_content_dir($backupfile, $shortname, $errors); + if (!empty($errors)) { + foreach ($errors as $key => $message) { + $this->error($key, $message); + } + return false; + } + if (empty($dir) && !empty($this->importoptions['restoredir'])) { $dir = $this->importoptions['restoredir']; } @@ -335,6 +350,15 @@ class tool_uploadcourse_course { return $dir; } + /** + * Return the errors found during preparation. + * + * @return array + */ + public function get_statuses() { + return $this->statuses; + } + /** * Return whether there were errors with this course. * @@ -355,7 +379,7 @@ class tool_uploadcourse_course { // Validate the shortname. if (!empty($this->shortname) || is_numeric($this->shortname)) { if ($this->shortname !== clean_param($this->shortname, PARAM_TEXT)) { - $this->error('invalid shortname'); + $this->error('invalidshortname', new lang_string('invalidshortname', 'tool_uploadcourse')); return false; } } @@ -365,10 +389,10 @@ class tool_uploadcourse_course { // Do we want to delete the course? if ($this->options['delete']) { if (!$exists) { - $this->error('cannot delete a course that does not exist'); + $this->error('cannotdeletecoursenotexist', new lang_string('cannotdeletecoursenotexist', 'tool_uploadcourse')); return false; } else if (!$this->can_delete()) { - $this->error('no permission to delete course'); + $this->error('coursedeletionnotallowed', new lang_string('coursedeletionnotallowed', 'tool_uploadcourse')); return false; } @@ -379,12 +403,13 @@ class tool_uploadcourse_course { // Can we create/update the course under those conditions? if ($exists) { if ($this->mode === tool_uploadcourse_processor::MODE_CREATE_NEW) { - $this->error('course exists and mode does not allow update'); + $this->error('courseexistsanduploadnotallowed', new lang_string('courseexistsanduploadnotallowed', 'tool_uploadcourse')); return false; } } else { if (!$this->can_create()) { - $this->error('course does not exist and mode does not allow creation'); + $this->error('coursedoesnotexistandcreatenotallowed', + new lang_string('coursedoesnotexistandcreatenotallowed', 'tool_uploadcourse')); return false; } } @@ -409,62 +434,72 @@ class tool_uploadcourse_course { if (!$exists || $mode === tool_uploadcourse_processor::MODE_CREATE_ALL) { // Mandatory fields upon creation. - $errors = 0; + $errors = array(); foreach (self::$mandatoryfields as $field) { if ((!isset($coursedata[$field]) || $coursedata[$field] === '') && (!isset($this->defaults[$field]) || $this->defaults[$field] === '')) { - $this->error('missing value for mandatory field: ' . $field); - $errors++; + $errors[] = $field; } } - if ($errors > 0) { + if (!empty($errors)) { + $this->error('missingmandatoryfields', new lang_string('missingmandatoryfields', 'tool_uploadcourse', + implode(', ', $errors))); return false; } } // Should the course be renamed? if (!empty($this->options['rename']) || is_numeric($this->options['rename'])) { - if (!$exists) { - $this->error('cannot rename a course that does not exist'); + if (!$this->can_update()) { + $this->error('canonlyrenameinupdatemode', new lang_string('canonlyrenameinupdatemode', 'tool_uploadcourse')); + return false; + } else if (!$exists) { + $this->error('cannotrenamecoursenotexist', new lang_string('cannotrenamecoursenotexist', 'tool_uploadcourse')); return false; } else if (!$this->can_rename()) { - $this->error('no permission to rename the course'); + $this->error('courserenamingnotallowed', new lang_string('courserenamingnotallowed', 'tool_uploadcourse')); return false; } else if ($this->options['rename'] !== clean_param($this->options['rename'], PARAM_TEXT)) { - $this->error('the shortname to rename the course with is invalid'); + $this->error('invalidshortname', new lang_string('invalidshortname', 'tool_uploadcourse')); return false; } else if ($this->exists($this->options['rename'])) { - $this->error('the shortname to rename the course with is already used'); + $this->error('cannotrenameshortnamealreadyinuse', + new lang_string('cannotrenameshortnamealreadyinuse', 'tool_uploadcourse')); return false; } else if (isset($coursedata['idnumber']) && $DB->count_records_select('course', 'idnumber = :idn AND shortname != :sn', array('idn' => $coursedata['idnumber'], 'sn' => $this->shortname)) > 0) { - $this->error('cannot rename the course as its ID number would conflict with another one'); + $this->error('cannotrenameidnumberconflict', new lang_string('cannotrenameidnumberconflict', 'tool_uploadcourse')); return false; } $coursedata['shortname'] = $this->options['rename']; + $this->status('courserenamed', new lang_string('courserenamed', 'tool_uploadcourse', + array('from' => $this->shortname, 'to' => $coursedata['shortname']))); } // Should we generate a shortname? if (empty($this->shortname) && !is_numeric($this->shortname)) { if (empty($this->importoptions['shortnametemplate'])) { - $this->error('missing shortname and could not find a shortname template'); + $this->error('missingshortnamenotemplate', new lang_string('missingshortnamenotemplate', 'tool_uploadcourse')); return false; } else if (!$this->can_only_create()) { - $this->error('cannot generate a shortname from template when mode allow updating'); + $this->error('cannotgenerateshortnameupdatemode', + new lang_string('cannotgenerateshortnameupdatemode', 'tool_uploadcourse')); return false; } else { $newshortname = tool_uploadcourse_helper::generate_shortname($coursedata, $this->importoptions['shortnametemplate']); if (is_null($newshortname)) { - $this->error('the generated shortname is invalid'); + $this->error('generatedshortnameinvalid', new lang_string('generatedshortnameinvalid', 'tool_uploadcourse')); return false; } else if ($this->exists($newshortname)) { if ($mode === tool_uploadcourse_processor::MODE_CREATE_NEW) { - $this->error('the generated shortname is used by another course'); + $this->error('generatedshortnamealreadyinuse', + new lang_string('generatedshortnamealreadyinuse', 'tool_uploadcourse')); return false; } $exists = true; } + $this->status('courseshortnamegenerated', new lang_string('courseshortnamegenerated', 'tool_uploadcourse')); $this->shortname = $newshortname; } } @@ -475,12 +510,14 @@ class tool_uploadcourse_course { $this->shortname = tool_uploadcourse_helper::increment_shortname($this->shortname); $exists = false; if ($this->shortname != $original) { - // TODO Log new shortname. + $this->status('courseshortnameincremented', new lang_string('courseshortnameincremented', 'tool_uploadcourse', + array('from' => $original, 'to' => $this->shortname))); if (isset($coursedata['idnumber'])) { $originalidn = $coursedata['idnumber']; $coursedata['idnumber'] = $this->increment_idnumber($coursedata['idnumber']); if ($originalidn != $coursedata['idnumber']) { - // TODO Log new idnumber. + $this->status('courseidnumberincremented', new lang_string('courseidnumberincremented', 'tool_uploadcourse', + array('from' => $originalidn, 'to' => $coursedata['idnumber']))); } } } @@ -491,35 +528,43 @@ class tool_uploadcourse_course { case tool_uploadcourse_processor::MODE_CREATE_NEW: case tool_uploadcourse_processor::MODE_CREATE_ALL: if ($exists) { - $this->error('the course exists and mode does not allow for update'); + $this->error('courseexistsanduploadnotallowed', + new lang_string('courseexistsanduploadnotallowed', 'tool_uploadcourse')); return false; } break; case tool_uploadcourse_processor::MODE_UPDATE_ONLY: if (!$exists) { - $this->error('the course does not exist and mode only allow for update'); + $this->error('coursedoesnotexistandcreatenotallowed', + new lang_string('coursedoesnotexistandcreatenotallowed', 'tool_uploadcourse')); return false; } // No break! case tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE: if ($exists) { if ($updatemode === tool_uploadcourse_processor::UPDATE_NOTHING) { - $this->error('the update mode does not allow for any field to be updated'); + $this->error('updatemodedoessettonothing', + new lang_string('updatemodedoessettonothing', 'tool_uploadcourse')); return false; } } break; default: // O_o Huh?! This should really never happen here! - $this->error('unknown import mode!'); + $this->error('unknownimportmode', new lang_string('unknownimportmode', 'tool_uploadcourse')); return false; } // Resolve the category. - // TODO Log resolve error. - $catid = tool_uploadcourse_helper::resolve_category($this->rawdata); - if (!empty($catid)) { + $errors = array(); + $catid = tool_uploadcourse_helper::resolve_category($this->rawdata, $errors); + if (!empty($catid) && empty($errors)) { $coursedata['category'] = $catid; + } else if (!empty($errors)) { + foreach ($errors as $key => $message) { + $this->error($key, $message); + } + return false; } // Get final data. @@ -538,15 +583,22 @@ class tool_uploadcourse_course { } // Add role renaming. - // TODO add warning about wrong roles. - foreach (tool_uploadcourse_helper::get_role_names($this->rawdata) as $rolekey => $rolename) { + $errors = array(); + $rolenames = tool_uploadcourse_helper::get_role_names($this->rawdata, $errors); + if (!empty($errors)) { + foreach ($errors as $key => $message) { + $this->error($key, $message); + } + return false; + } + foreach ($rolenames as $rolekey => $rolename) { $coursedata[$rolekey] = $rolename; } - // Validation. + // Some validation. if (!empty($coursedata['format']) && !in_array($coursedata['format'], tool_uploadcourse_helper::get_course_formats())) { - // Warning, wrong format! - $coursedata['format'] = 'weeks'; + $this->error('invalidcourseformat', new lang_string('invalidcourseformat', 'tool_uploadcourse')); + return false; } // Saving data. @@ -560,10 +612,11 @@ class tool_uploadcourse_course { // We cannot if ($this->importoptions['reset'] || $this->options['reset']) { if ($this->outcome !== self::OUTCOME_UPDATE) { - $this->error('can only reset a course that has been updated'); + $this->error('canonlyresetcourseinupdatemode', + new lang_string('canonlyresetcourseinupdatemode', 'tool_uploadcourse')); return false; } else if (!$this->can_reset()) { - $this->error('no permission to reset the course'); + $this->error('courseresetnotallowed', new lang_string('courseresetnotallowed', 'tool_uploadcourse')); return false; } } @@ -586,12 +639,18 @@ class tool_uploadcourse_course { } if ($this->outcome === self::OUTCOME_DELETE) { - return $this->delete(); + if ($this->delete()) { + $this->status('coursedeleted', new lang_string('coursedeleted', 'tool_uploadcourse')); + } else { + $this->error('errorwhiledeletingcourse', new lang_string('errorwhiledeletingcourse', 'tool_uploadcourse')); + } } else if ($this->outcome === self::OUTCOME_CREATE) { $course = create_course((object) $this->data); + $this->status('coursecreated', new lang_string('coursecreated', 'tool_uploadcourse')); } else if ($this->outcome === self::OUTCOME_UPDATE) { $course = (object) $this->data; update_course($course); + $this->status('courseupdated', new lang_string('courseupdated', 'tool_uploadcourse')); } else { // Strangely the outcome has not been defined, or is unknown! throw new coding_exception('Unknown outcome!'); @@ -608,9 +667,9 @@ class tool_uploadcourse_course { } if ($rc->execute_precheck()) { $rc->execute_plan(); + $this->status('courserestored', new lang_string('courserestored', 'tool_uploadcourse')); } else { - // Error! - // $rc->get_precheck_results()); + $this->error('errorwhilerestoringcourse', new lang_string('errorwhilerestoringthecourse', 'tool_uploadcourse')); } $rc->destroy(); } @@ -622,6 +681,7 @@ class tool_uploadcourse_course { if ($this->importoptions['reset'] || $this->options['reset']) { if ($this->outcome === self::OUTCOME_UPDATE && $this->can_reset()) { $this->reset($course); + $this->status('coursereset', new lang_string('coursereset', 'tool_uploadcourse')); } } @@ -777,4 +837,18 @@ class tool_uploadcourse_course { return reset_course_userdata($resetdata); } + /** + * Log a status + * + * @param string $code status code. + * @param lang_string $message status message. + * @return void + */ + protected function status($code, lang_string $message) { + if (array_key_exists($code, $this->statuses)) { + throw new coding_exception('Status code already defined'); + } + $this->statuses[$code] = $message; + } + } diff --git a/admin/tool/uploadcourse/classes/helper.php b/admin/tool/uploadcourse/classes/helper.php index 87f77353852..0e24a33a178 100644 --- a/admin/tool/uploadcourse/classes/helper.php +++ b/admin/tool/uploadcourse/classes/helper.php @@ -222,9 +222,10 @@ class tool_uploadcourse_helper { * * @param string $backupfile path to a backup file. * @param string $shortname shortname of a course. + * @param array $errors will be populated with errors found. * @return string|false false when the backup couldn't retrieved. */ - public static function get_restore_content_dir($backupfile = null, $shortname = null) { + public static function get_restore_content_dir($backupfile = null, $shortname = null, &$errors = array()) { global $CFG, $DB, $USER; $cachekey = null; @@ -243,12 +244,19 @@ class tool_uploadcourse_helper { // Use false instead of null because it would consider that the cache // key has not been set. $backupid = false; - if (!empty($backupfile) && is_readable($backupfile)) { - // Extracting the backup file. - $packer = get_file_packer('application/vnd.moodle.backup'); - $backupid = restore_controller::get_tempdir_name(SITEID, $USER->id); - $path = "$CFG->tempdir/backup/$backupid/"; - $result = $packer->extract_to_pathname($backupfile, $path); + if (!empty($backupfile)) { + if (!is_readable($backupfile)) { + $errors['cannotreadbackupfile'] = new lang_string('cannotreadbackupfile', 'tool_uploadcourse'); + } else { + // Extracting the backup file. + $packer = get_file_packer('application/vnd.moodle.backup'); + $backupid = restore_controller::get_tempdir_name(SITEID, $USER->id); + $path = "$CFG->tempdir/backup/$backupid/"; + $result = $packer->extract_to_pathname($backupfile, $path); + if (!$result) { + $errors['invalidbackupfile'] = new lang_string('invalidbackupfile', 'tool_uploadcourse'); + } + } } else if (!empty($shortname) || is_numeric($shortname)) { // Creating restore from an existing course. $courseid = $DB->get_field('course', 'id', array('shortname' => $shortname), IGNORE_MISSING); @@ -258,6 +266,9 @@ class tool_uploadcourse_helper { $bc->execute_plan(); $backupid = $bc->get_backupid(); $bc->destroy(); + } else { + $errors['coursetorestorefromdoesnotexist'] = + new lang_string('coursetorestorefromdoesnotexist', 'tool_uploadcourse'); } } self::$restorecontentcache[$cachekey] = $backupid; @@ -288,17 +299,19 @@ class tool_uploadcourse_helper { * Get the role renaming data from the passed data. * * @param array $data data to extract the names from. + * @param array $errors will be populated with errors found. * @return array where the key is the role_, the value is the new name. */ - public static function get_role_names($data) { + public static function get_role_names($data, &$errors = array()) { $rolenames = array(); $rolesids = self::get_role_ids(); + $invalidroles = array(); foreach ($data as $field => $value) { $matches = array(); if (preg_match('/^role_(.+)?$/', $field, $matches)) { if (!isset($rolesids[$matches[1]])) { - // Error! + $invalidroles[] = $matches[1]; continue; } $rolenames['role_' . $rolesids[$matches[1]]] = $value; @@ -306,6 +319,10 @@ class tool_uploadcourse_helper { } + if (!empty($invalidroles)) { + $errors['invalidroles'] = new lang_string('invalidroles', 'tool_uploadcourse', implode(', ', $invalidroles)); + } + // Roles names. return $rolenames; } @@ -363,25 +380,33 @@ class tool_uploadcourse_helper { * - category_path, array of categories from parent to child. * * @param array $data to resolve the category from. + * @param array $errors will be populated with errors found. * @return int category ID. */ - public static function resolve_category($data) { + public static function resolve_category($data, &$errors = array()) { global $DB; $catid = null; if (!empty($data['category'])) { - $category = coursecat::get($data['category'], IGNORE_MISSING); + $category = coursecat::get((int) $data['category'], IGNORE_MISSING); if (!empty($category)) { $catid = $category->id; + } else { + $errors['couldnotresolvecatgorybyid'] = + new lang_string('couldnotresolvecatgorybyid', 'tool_uploadcourse'); } } if (empty($catid) && !empty($data['category_idnumber'])) { $catid = self::resolve_category_by_idnumber($data['category_idnumber']); + $errors['couldnotresolvecatgorybyidnumber'] = + new lang_string('couldnotresolvecatgorybyidnumber', 'tool_uploadcourse'); } if (empty($catid) && !empty($data['category_path'])) { $catid = self::resolve_category_by_path(explode(' / ', $data['category_path'])); + $errors['couldnotresolvecatgorybypath'] = + new lang_string('couldnotresolvecatgorybypath', 'tool_uploadcourse'); } return $catid; diff --git a/admin/tool/uploadcourse/classes/processor.php b/admin/tool/uploadcourse/classes/processor.php index 9fb6253b839..80c1e8057d6 100644 --- a/admin/tool/uploadcourse/classes/processor.php +++ b/admin/tool/uploadcourse/classes/processor.php @@ -109,12 +109,12 @@ class tool_uploadcourse_processor { /** @var array CSV columns. */ protected $columns = array(); + /** @var array of errors where the key is the line number. */ + protected $errors = array(); + /** @var int line number. */ protected $linenb = 0; - /** @var int line data. */ - protected $linedata = array(); - /** @var bool whether the process has been started or not. */ protected $processstarted = false; @@ -166,6 +166,8 @@ class tool_uploadcourse_processor { $this->cir = $cir; $this->columns = $cir->get_columns(); $this->defaults = $defaults; + $this->validate(); + $this->reset(); } /** @@ -184,15 +186,15 @@ class tool_uploadcourse_processor { $this->linenb++; $data = $this->parse_line($line); - $course = self::get_course($data); + $course = $this->get_course($data); if ($course->prepare()) { $course->proceed(); } else { - print_object($course->get_errors()); + $this->log_error($course->get_errors()); } } - // $this->remove_restore_content(); + $this->remove_restore_content(); } /** @@ -201,7 +203,7 @@ class tool_uploadcourse_processor { * @param array $data data to import the course with. * @return tool_uploadcourse_course */ - public function get_course($data) { + protected function get_course($data) { $importoptions = array( 'candelete' => $this->allowdeletes, 'canrename' => $this->allowrenames, @@ -213,13 +215,22 @@ class tool_uploadcourse_processor { return new tool_uploadcourse_course($this->mode, $this->updatemode, $data, $this->defaults, $importoptions); } + /** + * Return the errors. + * + * @return array + */ + public function get_errors() { + return $this->errors; + } + /** * Get the directory of the object to restore. * * @param string $default directory to use if none found. * @return string subdirectory in $CFG->tempdir/backup/... */ - public function get_restore_content_dir() { + protected function get_restore_content_dir() { $backupfile = null; $shortname = null; @@ -233,6 +244,25 @@ class tool_uploadcourse_processor { return $dir; } + /** + * Log errors on the current line. + * + * @param array $errors array of errors + * @return void + */ + protected function log_error($errors) { + if (empty($errors)) { + return; + } + + foreach ($errors as $code => $langstring) { + if (!isset($this->errors[$this->linenb])) { + $this->errors[$this->linenb] = array(); + } + $this->errors[$this->linenb][$code] = $langstring; + } + } + /** * Parse a line to return an array(column => value) * @@ -253,13 +283,70 @@ class tool_uploadcourse_processor { return $data; } + /** + * Return a preview of the import. + * + * This only returns passed data, along with the errors. + * + * @param integer $rows number of rows to preview. + * @return array of preview data. + */ + public function preview($rows = 10) { + if ($this->processstarted) { + throw new coding_exception('Process has already been started'); + } + $this->processstarted = true; + + // Loop over the CSV lines. + $preview = array(); + while (($line = $this->cir->next()) && $rows > $this->linenb) { + $this->linenb++; + $data = $this->parse_line($line); + $course = $this->get_course($data); + $result = $course->prepare(); + if (!$result) { + $this->log_error($course->get_errors()); + } + $row = $data; + $preview[$this->linenb] = $row; + } + + $this->remove_restore_content(); + + return $preview; + } + /** * Delete the restore object. * * @return void */ protected function remove_restore_content() { - global $CFG; tool_uploadcourse_helper::clean_restore_content(); } + + /** + * Reset the current process. + * + * @return void. + */ + public function reset() { + $this->processstarted = false; + $this->linenb = 0; + $this->cir->init(); + $this->errors = array(); + } + + /** + * Validation. + * + * @return void + */ + protected function validate() { + if (empty($this->columns)) { + throw new moodle_exception('cannotreadtmpfile', 'error'); + } else if (count($this->columns) < 2) { + throw new moodle_exception('csvfewcolumns', 'error'); + } + } } diff --git a/admin/tool/uploadcourse/lang/en/tool_uploadcourse.php b/admin/tool/uploadcourse/lang/en/tool_uploadcourse.php index dad679517aa..058df85cb47 100644 --- a/admin/tool/uploadcourse/lang/en/tool_uploadcourse.php +++ b/admin/tool/uploadcourse/lang/en/tool_uploadcourse.php @@ -24,6 +24,45 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +$string['invalidshortname'] = 'Invalid shortname'; +$string['cannotdeletecoursenotexist'] = 'Cannot delete a course that does not exist'; +$string['cannotgenerateshortnameupdatemode'] = 'Cannot generate a shortname when updates are allowed'; +$string['cannotreadbackupfile'] = 'Cannot read the backup file'; +$string['cannotrenamecoursenotexist'] = 'Cannot rename a course that does not exist'; +$string['cannotrenameidnumberconflict'] = 'Cannot rename the course, the ID number conflicts with an existing course'; +$string['cannotrenameshortnamealreadyinuse'] = 'Cannot rename the course, the shortname is already used'; +$string['canonlyrenameinupdatemode'] = 'Can only rename a course when update is allowed'; +$string['canonlyresetcourseinupdatemode'] = 'Can only reset a course in update mode'; +$string['couldnotresolvecatgorybyid'] = 'Could not resolve category by ID'; +$string['couldnotresolvecatgorybyidnumber'] = 'Could not resolve category by ID number'; +$string['couldnotresolvecatgorybypath'] = 'Could not resolve category by path'; +$string['coursecreated'] = 'Course created'; +$string['coursedeleted'] = 'Course deleted'; +$string['coursedeletionnotallowed'] = 'Course deletion is not allowed'; +$string['coursedoesnotexistandcreatenotallowed'] = 'The course does not exist and creating course is not allowed'; +$string['courseexistsanduploadnotallowed'] = 'The course exists and update are not allowed'; +$string['courseidnumberincremented'] = 'Course ID number incremented {$a->from} -> {$a->to}'; +$string['courserenamed'] = 'Course renamed'; +$string['courserenamingnotallowed'] = 'Course renaming is not allowed'; +$string['coursereset'] = 'Course reset'; +$string['courseresetnotallowed'] = 'Course reset now allowed'; +$string['courserestored'] = 'Course restored'; +$string['courseshortnameincremented'] = 'Course shortname incremented {$a->from} -> {$a->to}'; +$string['courseshortnamegenerated'] = 'Course shortname generated'; +$string['coursetorestorefromdoesnotexist'] = 'The course to restore from does not exist'; +$string['courseupdated'] = 'Course updated'; +$string['errorwhilerestoringcourse'] = 'Error while restoring the course'; +$string['errorwhiledeletingcourse'] = 'Error while deleting the course'; +$string['generatedshortnameinvalid'] = 'The generated shortname is invalid'; +$string['generatedshortnamealreadyinuse'] = 'The generated shortname is already in use'; +$string['invalidbackupfile'] = 'Invalid backup file'; +$string['invalidcourseformat'] = 'Invalid course format'; +$string['invalidroles'] = 'Invalid role names: {$a}'; +$string['missingmandatoryfields'] = 'Missing value for mandatory fields: {$a}'; +$string['missingshortnamenotemplate'] = 'Missing shortname and shortname template not set'; +$string['updatemodedoessettonothing'] = 'Update mode does not allow anything to be updated'; +$string['unknownimportmode'] = 'Unknown import mode'; + $string['csvfileerror'] = 'There is something wrong with the format of the CSV file - please check the number of headings and columns match, and that the delimiter and file encoding are correct (don\t use comma-quoted as Moodle does not support it): {$a}'; $string['allowdeletes'] = 'Allow deletes'; $string['allowrenames'] = 'Allow renames'; diff --git a/admin/tool/uploadcourse/tests/course_test.php b/admin/tool/uploadcourse/tests/course_test.php index ebf5c586b01..42dd5c821d5 100644 --- a/admin/tool/uploadcourse/tests/course_test.php +++ b/admin/tool/uploadcourse/tests/course_test.php @@ -84,6 +84,7 @@ class tool_uploadcourse_course_testcase extends advanced_testcase { $data = array('shortname' => 'c1', 'fullname' => 'C1FN', 'summary' => 'C1', 'category' => 1); $co = new tool_uploadcourse_course($mode, $updatemode, $data); $this->assertFalse($co->prepare()); + $this->assertArrayHasKey('courseexistsanduploadnotallowed', $co->get_errors()); $this->assertEquals($coursecount, $DB->count_records('course', array())); $this->assertNotEquals('C1', $DB->get_field_select('course', 'summary', 'shortname = :s', array('s' => 'c1'))); @@ -114,6 +115,7 @@ class tool_uploadcourse_course_testcase extends advanced_testcase { $data = array('shortname' => $c1->shortname, 'delete' => 1); $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions); $this->assertFalse($co->prepare()); + $this->assertArrayHasKey('coursedeletionnotallowed', $co->get_errors()); $this->assertTrue($DB->record_exists('course', array('shortname' => $c1->shortname))); // Try delete when not requested. @@ -137,6 +139,7 @@ class tool_uploadcourse_course_testcase extends advanced_testcase { $data = array('shortname' => 'DoesNotExist', 'delete' => 1); $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions); $this->assertFalse($co->prepare()); + $this->assertArrayHasKey('cannotdeletecoursenotexist', $co->get_errors()); } public function test_update() { @@ -145,27 +148,28 @@ class tool_uploadcourse_course_testcase extends advanced_testcase { $c1 = $this->getDataGenerator()->create_course(array('shortname' => 'c1')); - // Try to add with existing shortnames, not allowing creation, and updating nothing. + // Try to update with existing shortnames, not allowing creation, and updating nothing. $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY; $updatemode = tool_uploadcourse_processor::UPDATE_NOTHING; $data = array('shortname' => 'c1', 'fullname' => 'New fullname'); $co = new tool_uploadcourse_course($mode, $updatemode, $data); $this->assertFalse($co->prepare()); + $this->assertArrayHasKey('updatemodedoessettonothing', $co->get_errors()); - // Try to add with non-existing shortnames. + // Try to update with non-existing shortnames. $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY; $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY; $data = array('shortname' => 'DoesNotExist', 'fullname' => 'New fullname'); $co = new tool_uploadcourse_course($mode, $updatemode, $data); $this->assertFalse($co->prepare()); + $this->assertArrayHasKey('coursedoesnotexistandcreatenotallowed', $co->get_errors()); // Try a proper update. $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY; $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY; $data = array('shortname' => 'c1', 'fullname' => 'New fullname'); $co = new tool_uploadcourse_course($mode, $updatemode, $data); - // $this->assertTrue($co->prepare()); - $co->prepare(); + $this->assertTrue($co->prepare()); $co->proceed(); $this->assertEquals('New fullname', $DB->get_field_select('course', 'fullname', 'shortname = :s', array('s' => 'c1'))); @@ -506,14 +510,16 @@ class tool_uploadcourse_course_testcase extends advanced_testcase { $data = array('shortname' => 'c1', 'rename' => 'newshortname'); $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions); $this->assertFalse($co->prepare()); + $this->assertArrayHasKey('courseexistsanduploadnotallowed', $co->get_errors()); // Cannot rename when creating. $mode = tool_uploadcourse_processor::MODE_CREATE_ALL; $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY; $importoptions = array('canrename' => true); - $data = array('shortname' => 'c1', 'rename' => 'newshortname'); + $data = array('shortname' => 'c1', 'rename' => 'newshortname', 'category' => 1, 'summary' => 'S', 'fullname' => 'F'); $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions); $this->assertFalse($co->prepare()); + $this->assertArrayHasKey('canonlyrenameinupdatemode', $co->get_errors()); // Error when not allowed to rename the course. $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY; @@ -522,6 +528,7 @@ class tool_uploadcourse_course_testcase extends advanced_testcase { $data = array('shortname' => 'c1', 'rename' => 'newshortname'); $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions); $this->assertFalse($co->prepare()); + $this->assertArrayHasKey('courserenamingnotallowed', $co->get_errors()); // Can rename when updating. $mode = tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE; @@ -544,12 +551,13 @@ class tool_uploadcourse_course_testcase extends advanced_testcase { $this->assertEquals('newshortname2', $DB->get_field_select('course', 'shortname', 'id = :id', array('id' => $c1->id))); // Error when course does not exist. - $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY; + $mode = tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE; $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY; $importoptions = array('canrename' => true); - $data = array('shortname' => 'DoesNotExist', 'rename' => 'c1'); + $data = array('shortname' => 'DoesNotExist', 'rename' => 'c1', 'category' => 1, 'summary' => 'S', 'fullname' => 'F'); $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions); $this->assertFalse($co->prepare()); + $this->assertArrayHasKey('cannotrenamecoursenotexist', $co->get_errors()); // Renaming still updates the other values. $mode = tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE; @@ -651,9 +659,10 @@ class tool_uploadcourse_course_testcase extends advanced_testcase { // Wrong mode. $mode = tool_uploadcourse_processor::MODE_CREATE_NEW; $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY; - $data = array('shortname' => $c1->shortname, 'reset' => '1'); + $data = array('shortname' => 'DoesNotExist', 'reset' => '1', 'summary' => 'summary', 'fullname' => 'FN', 'category' => 1); $co = new tool_uploadcourse_course($mode, $updatemode, $data); $this->assertFalse($co->prepare()); + $this->assertArrayHasKey('canonlyresetcourseinupdatemode', $co->get_errors()); $this->assertTrue($DB->record_exists('groups', array('id' => $g1->id))); $this->assertTrue($DB->record_exists('groups_members', array('groupid' => $g1->id, 'userid' => $u1->id))); $this->assertCount(1, get_enrolled_users($c1ctx)); @@ -665,6 +674,7 @@ class tool_uploadcourse_course_testcase extends advanced_testcase { $importoptions = array('canreset' => false); $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions); $this->assertFalse($co->prepare()); + $this->assertArrayHasKey('courseresetnotallowed', $co->get_errors()); $this->assertTrue($DB->record_exists('groups', array('id' => $g1->id))); $this->assertTrue($DB->record_exists('groups_members', array('groupid' => $g1->id, 'userid' => $u1->id))); $this->assertCount(1, get_enrolled_users($c1ctx)); @@ -676,6 +686,7 @@ class tool_uploadcourse_course_testcase extends advanced_testcase { $importoptions = array('canreset' => true); $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions); $this->assertTrue($co->prepare()); + $co->proceed(); $this->assertTrue($DB->record_exists('groups', array('id' => $g1->id))); $this->assertTrue($DB->record_exists('groups_members', array('groupid' => $g1->id, 'userid' => $u1->id))); $this->assertCount(1, get_enrolled_users($c1ctx)); diff --git a/admin/tool/uploadcourse/tests/helper_test.php b/admin/tool/uploadcourse/tests/helper_test.php index e36728ed097..344c13c815c 100644 --- a/admin/tool/uploadcourse/tests/helper_test.php +++ b/admin/tool/uploadcourse/tests/helper_test.php @@ -208,8 +208,10 @@ class tool_uploadcourse_helper_testcase extends advanced_testcase { 'role_' . $roleids['villain'] => 'Jabba the Hutt', ); - $actual = tool_uploadcourse_helper::get_role_names($data); + $errors = array(); + $actual = tool_uploadcourse_helper::get_role_names($data, $errors); $this->assertSame($actual, $expected); + $this->assertArrayHasKey('invalidroles', $errors); } public function test_increment_idnumber() { @@ -257,12 +259,18 @@ class tool_uploadcourse_helper_testcase extends advanced_testcase { $this->assertEquals($c2->id, tool_uploadcourse_helper::resolve_category($data)); // Adding unexisting data. + $errors = array(); $data['category_idnumber'] = 1234; - $this->assertEquals($c2->id, tool_uploadcourse_helper::resolve_category($data)); + $this->assertEquals($c2->id, tool_uploadcourse_helper::resolve_category($data, $errors)); + $this->assertArrayHasKey('couldnotresolvecatgorybyidnumber', $errors); + $errors = array(); $data['category'] = 1234; - $this->assertEquals($c2->id, tool_uploadcourse_helper::resolve_category($data)); + $this->assertEquals($c2->id, tool_uploadcourse_helper::resolve_category($data, $errors)); + $this->assertArrayHasKey('couldnotresolvecatgorybyid', $errors); + $errors = array(); $data['category_path'] = 'Not exist'; - $this->assertEmpty(tool_uploadcourse_helper::resolve_category($data)); + $this->assertEmpty(tool_uploadcourse_helper::resolve_category($data, $errors)); + $this->assertArrayHasKey('couldnotresolvecatgorybypath', $errors); } public function test_resolve_category_by_idnumber() {