MDL-13114 tool_uploadcourse: Improving the error reporting
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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_<id>, 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;
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user