Merge branch 'MDL-13114-master' of git://github.com/FMCorz/moodle

This commit is contained in:
Marina Glancy
2013-07-16 13:07:17 +10:00
21 changed files with 4509 additions and 1 deletions
@@ -0,0 +1,93 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* File containing the base import form.
*
* @package tool_uploadcourse
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir.'/formslib.php');
/**
* Base import form.
*
* @package tool_uploadcourse
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class tool_uploadcourse_base_form extends moodleform {
/**
* Empty definition.
*
* @return void
*/
public function definition() {
}
/**
* Adds the import settings part.
*
* @return void
*/
public function add_import_options() {
$mform = $this->_form;
// Upload settings and file.
$mform->addElement('header', 'importoptionshdr', get_string('importoptions', 'tool_uploadcourse'));
$mform->setExpanded('importoptionshdr', true);
$choices = array(
tool_uploadcourse_processor::MODE_CREATE_NEW => get_string('createnew', 'tool_uploadcourse'),
tool_uploadcourse_processor::MODE_CREATE_ALL => get_string('createall', 'tool_uploadcourse'),
tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE => get_string('createorupdate', 'tool_uploadcourse'),
tool_uploadcourse_processor::MODE_UPDATE_ONLY => get_string('updateonly', 'tool_uploadcourse')
);
$mform->addElement('select', 'options[mode]', get_string('mode', 'tool_uploadcourse'), $choices);
$choices = array(
tool_uploadcourse_processor::UPDATE_NOTHING => get_string('nochanges', 'tool_uploadcourse'),
tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY => get_string('updatewithdataonly', 'tool_uploadcourse'),
tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_OR_DEFAUTLS =>
get_string('updatewithdataordefaults', 'tool_uploadcourse'),
tool_uploadcourse_processor::UPDATE_MISSING_WITH_DATA_OR_DEFAUTLS => get_string('updatemissing', 'tool_uploadcourse')
);
$mform->addElement('select', 'options[updatemode]', get_string('updatemode', 'tool_uploadcourse'), $choices);
$mform->setDefault('options[updatemode]', tool_uploadcourse_processor::UPDATE_NOTHING);
$mform->disabledIf('options[updatemode]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_NEW);
$mform->disabledIf('options[updatemode]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_ALL);
$mform->addElement('selectyesno', 'options[allowdeletes]', get_string('allowdeletes', 'tool_uploadcourse'));
$mform->setDefault('options[allowdeletes]', 0);
$mform->disabledIf('options[allowdeletes]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_NEW);
$mform->disabledIf('options[allowdeletes]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_ALL);
$mform->addElement('selectyesno', 'options[allowrenames]', get_string('allowrenames', 'tool_uploadcourse'));
$mform->setDefault('options[allowrenames]', 0);
$mform->disabledIf('options[allowrenames]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_NEW);
$mform->disabledIf('options[allowrenames]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_ALL);
$mform->addElement('selectyesno', 'options[allowresets]', get_string('allowresets', 'tool_uploadcourse'));
$mform->setDefault('options[allowresets]', 0);
$mform->disabledIf('options[allowresets]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_NEW);
$mform->disabledIf('options[allowresets]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_ALL);
}
}
+898
View File
@@ -0,0 +1,898 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* File containing the course class.
*
* @package tool_uploadcourse
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
require_once($CFG->dirroot . '/course/lib.php');
/**
* Course class.
*
* @package tool_uploadcourse
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class tool_uploadcourse_course {
/** Outcome of the process: creating the course */
const DO_CREATE = 1;
/** Outcome of the process: updating the course */
const DO_UPDATE = 2;
/** Outcome of the process: deleting the course */
const DO_DELETE = 3;
/** @var array final import data. */
protected $data = array();
/** @var array default values. */
protected $defaults = array();
/** @var array enrolment data. */
protected $enrolmentdata;
/** @var array errors. */
protected $errors = array();
/** @var int the ID of the course that had been processed. */
protected $id;
/** @var array containing options passed from the processor. */
protected $importoptions = array();
/** @var int import mode. Matches tool_uploadcourse_processor::MODE_* */
protected $mode;
/** @var array course import options. */
protected $options = array();
/** @var int constant value of self::DO_*, what to do with that course */
protected $do;
/** @var bool set to true once we have prepared the course */
protected $prepared = false;
/** @var bool set to true once we have started the process of the course */
protected $processstarted = false;
/** @var array course import data. */
protected $rawdata = array();
/** @var array restore directory. */
protected $restoredata;
/** @var string course shortname. */
protected $shortname;
/** @var array errors. */
protected $statuses = array();
/** @var int update mode. Matches tool_uploadcourse_processor::UPDATE_* */
protected $updatemode;
/** @var array fields allowed as course data. */
static protected $validfields = array('fullname', 'shortname', 'idnumber', 'category', 'visible', 'startdate',
'summary', 'format', 'theme', 'lang', 'newsitems', 'showgrades', 'showreports', 'legacyfiles', 'maxbytes',
'groupmode', 'groupmodeforce', 'groupmodeforce', 'enablecompletion');
/** @var array fields required on course creation. */
static protected $mandatoryfields = array('fullname', 'category');
/** @var array fields which are considered as options. */
static protected $optionfields = array('delete' => false, 'rename' => null, 'backupfile' => null,
'templatecourse' => null, 'reset' => false);
/** @var array options determining what can or cannot be done at an import level. */
static protected $importoptionsdefaults = array('canrename' => false, 'candelete' => false, 'canreset' => false,
'reset' => false, 'restoredir' => null, 'shortnametemplate' => null);
/**
* Constructor
*
* @param int $mode import mode, constant matching tool_uploadcourse_processor::MODE_*
* @param int $updatemode update mode, constant matching tool_uploadcourse_processor::UPDATE_*
* @param array $rawdata raw course data.
* @param array $defaults default course data.
* @param array $importoptions import options.
*/
public function __construct($mode, $updatemode, $rawdata, $defaults = array(), $importoptions = array()) {
if ($mode !== tool_uploadcourse_processor::MODE_CREATE_NEW &&
$mode !== tool_uploadcourse_processor::MODE_CREATE_ALL &&
$mode !== tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE &&
$mode !== tool_uploadcourse_processor::MODE_UPDATE_ONLY) {
throw new coding_exception('Incorrect mode.');
} else if ($updatemode !== tool_uploadcourse_processor::UPDATE_NOTHING &&
$updatemode !== tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY &&
$updatemode !== tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_OR_DEFAUTLS &&
$updatemode !== tool_uploadcourse_processor::UPDATE_MISSING_WITH_DATA_OR_DEFAUTLS) {
throw new coding_exception('Incorrect update mode.');
}
$this->mode = $mode;
$this->updatemode = $updatemode;
if (isset($rawdata['shortname'])) {
$this->shortname = $rawdata['shortname'];
}
$this->rawdata = $rawdata;
$this->defaults = $defaults;
// Extract course options.
foreach (self::$optionfields as $option => $default) {
$this->options[$option] = isset($rawdata[$option]) ? $rawdata[$option] : $default;
}
// Import options.
foreach (self::$importoptionsdefaults as $option => $default) {
$this->importoptions[$option] = isset($importoptions[$option]) ? $importoptions[$option] : $default;
}
}
/**
* Does the mode allow for course creation?
*
* @return bool
*/
public function can_create() {
return in_array($this->mode, array(tool_uploadcourse_processor::MODE_CREATE_ALL,
tool_uploadcourse_processor::MODE_CREATE_NEW,
tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE)
);
}
/**
* Does the mode allow for course deletion?
*
* @return bool
*/
public function can_delete() {
return $this->importoptions['candelete'];
}
/**
* Does the mode only allow for course creation?
*
* @return bool
*/
public function can_only_create() {
return in_array($this->mode, array(tool_uploadcourse_processor::MODE_CREATE_ALL,
tool_uploadcourse_processor::MODE_CREATE_NEW));
}
/**
* Does the mode allow for course rename?
*
* @return bool
*/
public function can_rename() {
return $this->importoptions['canrename'];
}
/**
* Does the mode allow for course reset?
*
* @return bool
*/
public function can_reset() {
return $this->importoptions['canreset'];
}
/**
* Does the mode allow for course update?
*
* @return bool
*/
public function can_update() {
return in_array($this->mode,
array(
tool_uploadcourse_processor::MODE_UPDATE_ONLY,
tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE)
) && $this->updatemode != tool_uploadcourse_processor::UPDATE_NOTHING;
}
/**
* Can we use default values?
*
* @return bool
*/
public function can_use_defaults() {
return in_array($this->updatemode, array(tool_uploadcourse_processor::UPDATE_MISSING_WITH_DATA_OR_DEFAUTLS,
tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_OR_DEFAUTLS));
}
/**
* Delete the current course.
*
* @return bool
*/
protected function delete() {
global $DB;
$this->id = $DB->get_field_select('course', 'id', 'shortname = :shortname',
array('shortname' => $this->shortname), MUST_EXIST);
return delete_course($this->id, false);
}
/**
* Log an error
*
* @param string $code error code.
* @param lang_string $message error message.
* @return void
*/
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;
}
/**
* Return whether the course exists or not.
*
* @param string $shortname the shortname to use to check if the course exists. Falls back on $this->shortname if empty.
* @return bool
*/
protected function exists($shortname = null) {
global $DB;
if (is_null($shortname)) {
$shortname = $this->shortname;
}
if (!empty($shortname) || is_numeric($shortname)) {
return $DB->record_exists('course', array('shortname' => $shortname));
}
return false;
}
/**
* Return the data that will be used upon saving.
*
* @return null|array
*/
public function get_data() {
return $this->data;
}
/**
* Return the errors found during preparation.
*
* @return array
*/
public function get_errors() {
return $this->errors;
}
/**
* Assemble the course data based on defaults.
*
* This returns the final data to be passed to create_course().
*
* @param array $data current data.
* @return array
*/
protected function get_final_create_data($data) {
foreach (self::$validfields as $field) {
if (!isset($data[$field]) && isset($this->defaults[$field])) {
$data[$field] = $this->defaults[$field];
}
}
$data['shortname'] = $this->shortname;
return $data;
}
/**
* Assemble the course data based on defaults.
*
* This returns the final data to be passed to update_course().
*
* @param array $data current data.
* @param bool $usedefaults are defaults allowed?
* @param bool $missingonly ignore fields which are already set.
* @return array
*/
protected function get_final_update_data($data, $usedefaults = false, $missingonly = false) {
global $DB;
$newdata = array();
$existingdata = $DB->get_record('course', array('shortname' => $this->shortname));
foreach (self::$validfields as $field) {
if ($missingonly) {
if (!is_null($existingdata->$field) and $existingdata->$field !== '') {
continue;
}
}
if (isset($data[$field])) {
$newdata[$field] = $data[$field];
} else if ($usedefaults && isset($this->defaults[$field])) {
$newdata[$field] = $this->defaults[$field];
}
}
$newdata['id'] = $existingdata->id;
return $newdata;
}
/**
* Return the ID of the processed course.
*
* @return int|null
*/
public function get_id() {
if (!$this->processstarted) {
throw new coding_exception('The course has not been processed yet!');
}
return $this->id;
}
/**
* Get the directory of the object to restore.
*
* @return string|false subdirectory in $CFG->tempdir/backup/...
*/
protected function get_restore_content_dir() {
$backupfile = null;
$shortname = null;
if (!empty($this->options['backupfile'])) {
$backupfile = $this->options['backupfile'];
} else if (!empty($this->options['templatecourse']) || is_numeric($this->options['templatecourse'])) {
$shortname = $this->options['templatecourse'];
}
$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'];
}
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.
*
* @return boolean
*/
public function has_errors() {
return !empty($this->errors);
}
/**
* Validates and prepares the data.
*
* @return bool false is any error occured.
*/
public function prepare() {
global $DB;
$this->prepared = true;
// Validate the shortname.
if (!empty($this->shortname) || is_numeric($this->shortname)) {
if ($this->shortname !== clean_param($this->shortname, PARAM_TEXT)) {
$this->error('invalidshortname', new lang_string('invalidshortname', 'tool_uploadcourse'));
return false;
}
}
$exists = $this->exists();
// Do we want to delete the course?
if ($this->options['delete']) {
if (!$exists) {
$this->error('cannotdeletecoursenotexist', new lang_string('cannotdeletecoursenotexist', 'tool_uploadcourse'));
return false;
} else if (!$this->can_delete()) {
$this->error('coursedeletionnotallowed', new lang_string('coursedeletionnotallowed', 'tool_uploadcourse'));
return false;
}
$this->do = self::DO_DELETE;
return true;
}
// Can we create/update the course under those conditions?
if ($exists) {
if ($this->mode === tool_uploadcourse_processor::MODE_CREATE_NEW) {
$this->error('courseexistsanduploadnotallowed',
new lang_string('courseexistsanduploadnotallowed', 'tool_uploadcourse'));
return false;
}
} else {
if (!$this->can_create()) {
$this->error('coursedoesnotexistandcreatenotallowed',
new lang_string('coursedoesnotexistandcreatenotallowed', 'tool_uploadcourse'));
return false;
}
}
// Basic data.
$coursedata = array();
foreach ($this->rawdata as $field => $value) {
if (!in_array($field, self::$validfields)) {
continue;
} else if ($field == 'shortname') {
// Let's leave it apart from now, use $this->shortname only.
continue;
}
$coursedata[$field] = $value;
}
$mode = $this->mode;
$updatemode = $this->updatemode;
$usedefaults = $this->can_use_defaults();
// Resolve the category, and fail if not found.
$errors = array();
$catid = tool_uploadcourse_helper::resolve_category($this->rawdata, $errors);
if (empty($errors)) {
$coursedata['category'] = $catid;
} else {
foreach ($errors as $key => $message) {
$this->error($key, $message);
}
return false;
}
// If the course does not exist, or will be forced created.
if (!$exists || $mode === tool_uploadcourse_processor::MODE_CREATE_ALL) {
// Mandatory fields upon creation.
$errors = array();
foreach (self::$mandatoryfields as $field) {
if ((!isset($coursedata[$field]) || $coursedata[$field] === '') &&
(!isset($this->defaults[$field]) || $this->defaults[$field] === '')) {
$errors[] = $field;
}
}
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 (!$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('courserenamingnotallowed', new lang_string('courserenamingnotallowed', 'tool_uploadcourse'));
return false;
} else if ($this->options['rename'] !== clean_param($this->options['rename'], PARAM_TEXT)) {
$this->error('invalidshortname', new lang_string('invalidshortname', 'tool_uploadcourse'));
return false;
} else if ($this->exists($this->options['rename'])) {
$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('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('missingshortnamenotemplate', new lang_string('missingshortnamenotemplate', 'tool_uploadcourse'));
return false;
} else if (!$this->can_only_create()) {
$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('generatedshortnameinvalid', new lang_string('generatedshortnameinvalid', 'tool_uploadcourse'));
return false;
} else if ($this->exists($newshortname)) {
if ($mode === tool_uploadcourse_processor::MODE_CREATE_NEW) {
$this->error('generatedshortnamealreadyinuse',
new lang_string('generatedshortnamealreadyinuse', 'tool_uploadcourse'));
return false;
}
$exists = true;
}
$this->status('courseshortnamegenerated', new lang_string('courseshortnamegenerated', 'tool_uploadcourse',
$newshortname));
$this->shortname = $newshortname;
}
}
// If exists, but we only want to create courses, increment the shortname.
if ($exists && $mode === tool_uploadcourse_processor::MODE_CREATE_ALL) {
$original = $this->shortname;
$this->shortname = tool_uploadcourse_helper::increment_shortname($this->shortname);
$exists = false;
if ($this->shortname != $original) {
$this->status('courseshortnameincremented', new lang_string('courseshortnameincremented', 'tool_uploadcourse',
array('from' => $original, 'to' => $this->shortname)));
if (isset($coursedata['idnumber'])) {
$originalidn = $coursedata['idnumber'];
$coursedata['idnumber'] = tool_uploadcourse_helper::increment_idnumber($coursedata['idnumber']);
if ($originalidn != $coursedata['idnumber']) {
$this->status('courseidnumberincremented', new lang_string('courseidnumberincremented', 'tool_uploadcourse',
array('from' => $originalidn, 'to' => $coursedata['idnumber'])));
}
}
}
}
// If the course does not exist, ensure that the ID number is not taken.
if (!$exists && isset($coursedata['idnumber'])) {
if ($DB->count_records_select('course', 'idnumber = :idn', array('idn' => $coursedata['idnumber'])) > 0) {
$this->error('idnumberalreadyinuse', new lang_string('idnumberalreadyinuse', 'tool_uploadcourse'));
return false;
}
}
// Ultimate check mode vs. existence.
switch ($mode) {
case tool_uploadcourse_processor::MODE_CREATE_NEW:
case tool_uploadcourse_processor::MODE_CREATE_ALL:
if ($exists) {
$this->error('courseexistsanduploadnotallowed',
new lang_string('courseexistsanduploadnotallowed', 'tool_uploadcourse'));
return false;
}
break;
case tool_uploadcourse_processor::MODE_UPDATE_ONLY:
if (!$exists) {
$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('updatemodedoessettonothing',
new lang_string('updatemodedoessettonothing', 'tool_uploadcourse'));
return false;
}
}
break;
default:
// O_o Huh?! This should really never happen here!
$this->error('unknownimportmode', new lang_string('unknownimportmode', 'tool_uploadcourse'));
return false;
}
// Get final data.
if ($exists) {
$missingonly = ($updatemode === tool_uploadcourse_processor::UPDATE_MISSING_WITH_DATA_OR_DEFAUTLS);
$coursedata = $this->get_final_update_data($coursedata, $usedefaults, $missingonly);
$this->do = self::DO_UPDATE;
} else {
$coursedata = $this->get_final_create_data($coursedata);
$this->do = self::DO_CREATE;
}
// Course start date.
if (!empty($coursedata['startdate'])) {
$coursedata['startdate'] = strtotime($coursedata['startdate']);
}
// Add role renaming.
$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;
}
// Some validation.
if (!empty($coursedata['format']) && !in_array($coursedata['format'], tool_uploadcourse_helper::get_course_formats())) {
$this->error('invalidcourseformat', new lang_string('invalidcourseformat', 'tool_uploadcourse'));
return false;
}
// Saving data.
$this->data = $coursedata;
$this->enrolmentdata = tool_uploadcourse_helper::get_enrolment_data($this->rawdata);
// Restore data.
// TODO log warnings.
$this->restoredata = $this->get_restore_content_dir();
// We can only reset courses when allowed and we are updating the course.
if ($this->importoptions['reset'] || $this->options['reset']) {
if ($this->do !== self::DO_UPDATE) {
$this->error('canonlyresetcourseinupdatemode',
new lang_string('canonlyresetcourseinupdatemode', 'tool_uploadcourse'));
return false;
} else if (!$this->can_reset()) {
$this->error('courseresetnotallowed', new lang_string('courseresetnotallowed', 'tool_uploadcourse'));
return false;
}
}
return true;
}
/**
* Proceed with the import of the course.
*
* @return void
*/
public function proceed() {
global $CFG, $USER;
if (!$this->prepared) {
throw new coding_exception('The course has not been prepared.');
} else if ($this->has_errors()) {
throw new moodle_exception('Cannot proceed, errors were detected.');
} else if ($this->processstarted) {
throw new coding_exception('The process has already been started.');
}
$this->processstarted = true;
if ($this->do === self::DO_DELETE) {
if ($this->delete()) {
$this->status('coursedeleted', new lang_string('coursedeleted', 'tool_uploadcourse'));
} else {
$this->error('errorwhiledeletingcourse', new lang_string('errorwhiledeletingcourse', 'tool_uploadcourse'));
}
return true;
} else if ($this->do === self::DO_CREATE) {
$course = create_course((object) $this->data);
$this->id = $course->id;
$this->status('coursecreated', new lang_string('coursecreated', 'tool_uploadcourse'));
} else if ($this->do === self::DO_UPDATE) {
$course = (object) $this->data;
update_course($course);
$this->id = $course->id;
$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!');
}
// Restore a course.
if (!empty($this->restoredata)) {
$rc = new restore_controller($this->restoredata, $course->id, backup::INTERACTIVE_NO,
backup::MODE_IMPORT, $USER->id, backup::TARGET_CURRENT_ADDING);
// Check if the format conversion must happen first.
if ($rc->get_status() == backup::STATUS_REQUIRE_CONV) {
$rc->convert();
}
if ($rc->execute_precheck()) {
$rc->execute_plan();
$this->status('courserestored', new lang_string('courserestored', 'tool_uploadcourse'));
} else {
$this->error('errorwhilerestoringcourse', new lang_string('errorwhilerestoringthecourse', 'tool_uploadcourse'));
}
$rc->destroy();
}
// Proceed with enrolment data.
$this->process_enrolment_data($course);
// Reset the course.
if ($this->importoptions['reset'] || $this->options['reset']) {
if ($this->do === self::DO_UPDATE && $this->can_reset()) {
$this->reset($course);
$this->status('coursereset', new lang_string('coursereset', 'tool_uploadcourse'));
}
}
// Mark context as dirty.
$context = context_course::instance($course->id);
$context->mark_dirty();
}
/**
* Add the enrolment data for the course.
*
* @param object $course course record.
* @return void
*/
protected function process_enrolment_data($course) {
global $DB;
$enrolmentdata = $this->enrolmentdata;
if (empty($enrolmentdata)) {
return;
}
$enrolmentplugins = tool_uploadcourse_helper::get_enrolment_plugins();
$instances = enrol_get_instances($course->id, false);
foreach ($enrolmentdata as $enrolmethod => $method) {
$instance = null;
foreach ($instances as $i) {
if ($i->enrol == $enrolmethod) {
$instance = $i;
break;
}
}
$todelete = isset($method['delete']) && $method['delete'];
$todisable = isset($method['disable']) && $method['disable'];
unset($method['delete']);
unset($method['disable']);
if (!empty($instance) && $todelete) {
// Remove the enrolment method.
foreach ($instances as $instance) {
if ($instance->enrol == $enrolmethod) {
$plugin = $enrolmentplugins[$instance->enrol];
$plugin->delete_instance($instance);
break;
}
}
} else if (!empty($instance) && $todisable) {
// Disable the enrolment.
foreach ($instances as $instance) {
if ($instance->enrol == $enrolmethod) {
$plugin = $enrolmentplugins[$instance->enrol];
$plugin->update_status($instance, ENROL_INSTANCE_DISABLED);
$enrol_updated = true;
break;
}
}
} else {
$plugin = null;
if (empty($instance)) {
$plugin = $enrolmentplugins[$enrolmethod];
$instance = new stdClass();
$instance->id = $plugin->add_default_instance($course);
$instance->roleid = $plugin->get_config('roleid');
$instance->status = ENROL_INSTANCE_ENABLED;
} else {
$plugin = $enrolmentplugins[$instance->enrol];
$plugin->update_status($instance, ENROL_INSTANCE_ENABLED);
}
// Now update values.
foreach ($method as $k => $v) {
$instance->{$k} = $v;
}
// Sort out the start, end and date.
$instance->enrolstartdate = (isset($method['startdate']) ? strtotime($method['startdate']) : 0);
$instance->enrolenddate = (isset($method['enddate']) ? strtotime($method['enddate']) : 0);
// Is the enrolment period set?
if (isset($method['enrolperiod']) && ! empty($method['enrolperiod'])) {
if (preg_match('/^\d+$/', $method['enrolperiod'])) {
$method['enrolperiod'] = (int) $method['enrolperiod'];
} else {
// Try and convert period to seconds.
$method['enrolperiod'] = strtotime('1970-01-01 GMT + ' . $method['enrolperiod']);
}
$instance->enrolperiod = $method['enrolperiod'];
}
if ($instance->enrolstartdate > 0 && isset($method['enrolperiod'])) {
$instance->enrolenddate = $instance->enrolstartdate + $method['enrolperiod'];
}
if ($instance->enrolenddate > 0) {
$instance->enrolperiod = $instance->enrolenddate - $instance->enrolstartdate;
}
if ($instance->enrolenddate < $instance->enrolstartdate) {
$instance->enrolenddate = $instance->enrolstartdate;
}
// Sort out the given role. This does not filter the roles allowed in the course.
if (isset($method['role'])) {
$roleids = tool_uploadcourse_helper::get_role_ids();
if (isset($roleids[$method['role']])) {
$instance->roleid = $roleids[$method['role']];
}
}
$instance->timemodified = time();
$DB->update_record('enrol', $instance);
}
}
}
/**
* Reset the current course.
*
* This does not reset any of the content of the activities.
*
* @param stdClass $course the course object of the course to reset.
* @return array status array of array component, item, error.
*/
protected function reset($course) {
global $DB;
$resetdata = new stdClass();
$resetdata->id = $course->id;
$resetdata->reset_start_date = time();
$resetdata->reset_logs = true;
$resetdata->reset_events = true;
$resetdata->reset_notes = true;
$resetdata->delete_blog_associations = true;
$resetdata->reset_completion = true;
$resetdata->reset_roles_overrides = true;
$resetdata->reset_roles_local = true;
$resetdata->reset_groups_members = true;
$resetdata->reset_groups_remove = true;
$resetdata->reset_groupings_members = true;
$resetdata->reset_groupings_remove = true;
$resetdata->reset_gradebook_items = true;
$resetdata->reset_gradebook_grades = true;
$resetdata->reset_comments = true;
if (empty($course->startdate)) {
$course->startdate = $DB->get_field_select('course', 'startdate', 'id = :id', array('id' => $course->id));
}
$resetdata->reset_start_date_old = $course->startdate;
// Add roles.
$roles = tool_uploadcourse_helper::get_role_ids();
$resetdata->unenrol_users = array_values($roles);
$resetdata->unenrol_users[] = 0; // Enrolled without role.
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;
}
}
+486
View File
@@ -0,0 +1,486 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* File containing the helper class.
*
* @package tool_uploadcourse
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/coursecatlib.php');
require_once($CFG->dirroot . '/cache/lib.php');
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
/**
* Class containing a set of helpers.
*
* @package tool_uploadcourse
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class tool_uploadcourse_helper {
/**
* Remove the restore content from disk and cache.
*
* @return void
*/
public static function clean_restore_content() {
global $CFG;
if (!empty($CFG->keeptempdirectoriesonbackup)) {
$cache = cache::make('tool_uploadcourse', 'helper');
$backupids = (array) $cache->get('backupids');
foreach ($backupids as $cachekey => $backupid) {
$cache->delete($cachekey);
fulldelete("$CFG->tempdir/backup/$backupid/");
}
$cache->delete('backupids');
}
}
/**
* Generate a shortname based on a template.
*
* @param array|object $data course data.
* @param string $templateshortname template of shortname.
* @return null|string shortname based on the template, or null when an error occured.
*/
public static function generate_shortname($data, $templateshortname) {
if (empty($templateshortname) && !is_numeric($templateshortname)) {
return null;
}
if (strpos($templateshortname, '%') === false) {
return $templateshortname;
}
$course = (object) $data;
$fullname = isset($course->fullname) ? $course->fullname : '';
$idnumber = isset($course->idnumber) ? $course->idnumber : '';
$callback = partial(array('tool_uploadcourse_helper', 'generate_shortname_callback'), $fullname, $idnumber);
$result = preg_replace_callback('/(?<!%)%([+~-])?(\d)*([fi])/', $callback, $templateshortname);
if (!is_null($result)) {
$result = clean_param($result, PARAM_TEXT);
}
if (empty($result) && !is_numeric($result)) {
$result = null;
}
return $result;
}
/**
* Callback used when generating a shortname based on a template.
*
* @param string $fullname full name.
* @param string $idnumber ID number.
* @param array $block result from preg_replace_callback.
* @return string
*/
public static function generate_shortname_callback($fullname, $idnumber, $block) {
switch ($block[3]) {
case 'f':
$repl = $fullname;
break;
case 'i':
$repl = $idnumber;
break;
default:
return $block[0];
}
switch ($block[1]) {
case '+':
$repl = textlib::strtoupper($repl);
break;
case '-':
$repl = textlib::strtolower($repl);
break;
case '~':
$repl = textlib::strtotitle($repl);
break;
}
if (!empty($block[2])) {
$repl = textlib::substr($repl, 0, $block[2]);
}
return $repl;
}
/**
* Return the available course formats.
*
* @return array
*/
public static function get_course_formats() {
return array_keys(core_component::get_plugin_list('format'));
}
/**
* Extract enrolment data from passed data.
*
* Constructs an array of methods, and their options:
* array(
* 'method1' => array(
* 'option1' => value,
* 'option2' => value
* ),
* 'method2' => array(
* 'option1' => value,
* 'option2' => value
* )
* )
*
* @param array $data data to extract the enrolment data from.
* @return array
*/
public static function get_enrolment_data($data) {
$enrolmethods = array();
$enroloptions = array();
foreach ($data as $field => $value) {
// Enrolmnent data.
$matches = array();
if (preg_match('/^enrolment_(\d+)(_(.+))?$/', $field, $matches)) {
$key = $matches[1];
if (!isset($enroloptions[$key])) {
$enroloptions[$key] = array();
}
if (empty($matches[3])) {
$enrolmethods[$key] = $value;
} else {
$enroloptions[$key][$matches[3]] = $value;
}
}
}
// Combining enrolment methods and their options in a single array.
$enrolmentdata = array();
if (!empty($enrolmethods)) {
$enrolmentplugins = self::get_enrolment_plugins();
foreach ($enrolmethods as $key => $method) {
if (!array_key_exists($method, $enrolmentplugins)) {
// Error!
continue;
}
$enrolmentdata[$enrolmethods[$key]] = $enroloptions[$key];
}
}
return $enrolmentdata;
}
/**
* Return the enrolment plugins.
*
* The result is cached for faster execution.
*
* @return array
*/
public static function get_enrolment_plugins() {
$cache = cache::make('tool_uploadcourse', 'helper');
if (($enrol = $cache->get('enrol')) === false) {
$enrol = enrol_get_plugins(false);
$cache->set('enrol', $enrol);
}
return $enrol;
}
/**
* Get the restore content tempdir.
*
* The tempdir is the sub directory in which the backup has been extracted.
* This caches the result for better performance.
*
* @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, &$errors = array()) {
global $CFG, $DB, $USER;
$cachekey = null;
if (!empty($backupfile)) {
$backupfile = realpath($backupfile);
$cachekey = 'backup_path:' . $backupfile;
} else if (!empty($shortname) || is_numeric($shortname)) {
$cachekey = 'backup_sn:' . $shortname;
}
if (empty($cachekey)) {
return false;
}
$cache = cache::make('tool_uploadcourse', 'helper');
if (($backupid = $cache->get($cachekey)) === false) {
// Use false instead of null because it would consider that the cache
// key has not been set.
$backupid = false;
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);
if (!empty($courseid)) {
$bc = new backup_controller(backup::TYPE_1COURSE, $courseid, backup::FORMAT_MOODLE,
backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id);
$bc->execute_plan();
$backupid = $bc->get_backupid();
$bc->destroy();
} else {
$errors['coursetorestorefromdoesnotexist'] =
new lang_string('coursetorestorefromdoesnotexist', 'tool_uploadcourse');
}
}
$cache->set($cachekey, $backupid);
// Store all the directories to be able to remove them in self::clean_restore_content().
$backupids = (array) $cache->get('backupids');
$backupids[$cachekey] = $backupid;
$cache->set('backupids', $backupids);
}
return $backupid;
}
/**
* Return the role IDs.
*
* The result is cached for faster execution.
*
* @return array
*/
public static function get_role_ids() {
$cache = cache::make('tool_uploadcourse', 'helper');
if (($roles = $cache->get('roles')) === false) {
$roles = array();
$rolesraw = get_all_roles();
foreach ($rolesraw as $role) {
$roles[$role->shortname] = $role->id;
}
$cache->set('roles', $roles);
}
return $roles;
}
/**
* 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, &$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]])) {
$invalidroles[] = $matches[1];
continue;
}
$rolenames['role_' . $rolesids[$matches[1]]] = $value;
}
}
if (!empty($invalidroles)) {
$errors['invalidroles'] = new lang_string('invalidroles', 'tool_uploadcourse', implode(', ', $invalidroles));
}
// Roles names.
return $rolenames;
}
/**
* Helper to increment an ID number.
*
* This first checks if the ID number is in use.
*
* @param string $idnumber ID number to increment.
* @return string new ID number.
*/
public static function increment_idnumber($idnumber) {
global $DB;
while ($DB->record_exists('course', array('idnumber' => $idnumber))) {
$matches = array();
if (!preg_match('/(.*?)([0-9]+)$/', $idnumber, $matches)) {
$newidnumber = $idnumber . '_2';
} else {
$newidnumber = $matches[1] . ((int) $matches[2] + 1);
}
$idnumber = $newidnumber;
}
return $idnumber;
}
/**
* Helper to increment a shortname.
*
* This considers that the shortname passed has to be incremented.
*
* @param string $shortname shortname to increment.
* @return string new shortname.
*/
public static function increment_shortname($shortname) {
global $DB;
do {
$matches = array();
if (!preg_match('/(.*?)([0-9]+)$/', $shortname, $matches)) {
$newshortname = $shortname . '_2';
} else {
$newshortname = $matches[1] . ($matches[2]+1);
}
$shortname = $newshortname;
} while ($DB->record_exists('course', array('shortname' => $shortname)));
return $shortname;
}
/**
* Resolve a category based on the data passed.
*
* Key accepted are:
* - category, which is supposed to be a category ID.
* - category_idnumber
* - 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, &$errors = array()) {
$catid = null;
if (!empty($data['category'])) {
$category = coursecat::get((int) $data['category'], IGNORE_MISSING);
if (!empty($category) && !empty($category->id)) {
$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']);
if (empty($catid)) {
$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']));
if (empty($catid)) {
$errors['couldnotresolvecatgorybypath'] =
new lang_string('couldnotresolvecatgorybypath', 'tool_uploadcourse');
}
}
return $catid;
}
/**
* Resolve a category by ID number.
*
* @param string $idnumber category ID number.
* @return int category ID.
*/
public static function resolve_category_by_idnumber($idnumber) {
global $DB;
$cache = cache::make('tool_uploadcourse', 'helper');
$cachekey = 'cat_idn_' . $idnumber;
if (($id = $cache->get($cachekey)) === false) {
$params = array('idnumber' => $idnumber);
$id = $DB->get_field_select('course_categories', 'id', 'idnumber = :idnumber', $params, IGNORE_MISSING);
// Little hack to be able to differenciate between the cache not set and a category not found.
if ($id === false) {
$id = -1;
}
$cache->set($cachekey, $id);
}
// Little hack to be able to differenciate between the cache not set and a category not found.
if ($id == -1) {
$id = false;
}
return $id;
}
/**
* Resolve a category by path.
*
* @param array $path category names indexed from parent to children.
* @return int category ID.
*/
public static function resolve_category_by_path(array $path) {
global $DB;
$cache = cache::make('tool_uploadcourse', 'helper');
$cachekey = 'cat_path_' . serialize($path);
if (($id = $cache->get($cachekey)) === false) {
$parent = 0;
$sql = 'name = :name AND parent = :parent';
while ($name = array_shift($path)) {
$params = array('name' => $name, 'parent' => $parent);
if ($records = $DB->get_records_select('course_categories', $sql, $params, null, 'id, parent')) {
if (count($records) > 1) {
// Too many records with the same name!
$id = -1;
break;
}
$record = reset($records);
$id = $record->id;
$parent = $record->id;
} else {
// Not found.
$id = -1;
break;
}
}
$cache->set($cachekey, $id);
}
// We save -1 when the category has not been found to be able to know if the cache was set.
if ($id == -1) {
$id = false;
}
return $id;
}
}
@@ -0,0 +1,390 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* File containing processor class.
*
* @package tool_uploadcourse
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/csvlib.class.php');
/**
* Processor class.
*
* @package tool_uploadcourse
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class tool_uploadcourse_processor {
/**
* Create courses that do not exist yet.
*/
const MODE_CREATE_NEW = 1;
/**
* Create all courses, appending a suffix to the shortname if the course exists.
*/
const MODE_CREATE_ALL = 2;
/**
* Create courses, and update the ones that already exist.
*/
const MODE_CREATE_OR_UPDATE = 3;
/**
* Only update existing courses.
*/
const MODE_UPDATE_ONLY = 4;
/**
* During update, do not update anything... O_o Huh?!
*/
const UPDATE_NOTHING = 0;
/**
* During update, only use data passed from the CSV.
*/
const UPDATE_ALL_WITH_DATA_ONLY = 1;
/**
* During update, use either data from the CSV, or defaults.
*/
const UPDATE_ALL_WITH_DATA_OR_DEFAUTLS = 2;
/**
* During update, update missing values from either data from the CSV, or defaults.
*/
const UPDATE_MISSING_WITH_DATA_OR_DEFAUTLS = 3;
/** @var int processor mode. */
protected $mode;
/** @var int upload mode. */
protected $updatemode;
/** @var bool are renames allowed. */
protected $allowrenames = false;
/** @var bool are deletes allowed. */
protected $allowdeletes = false;
/** @var bool are resets allowed. */
protected $allowresets = false;
/** @var string path to a restore file. */
protected $restorefile;
/** @var string shortname of the course to be restored. */
protected $templatecourse;
/** @var string reset courses after processing them. */
protected $reset;
/** @var string template to generate a course shortname. */
protected $shortnametemplate;
/** @var csv_import_reader */
protected $cir;
/** @var array default values. */
protected $defaults = array();
/** @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 bool whether the process has been started or not. */
protected $processstarted = false;
/**
* Constructor
*
* @param csv_import_reader $cir import reader object
* @param array $options options of the process
* @param array $defaults default data value
*/
public function __construct(csv_import_reader $cir, array $options, array $defaults = array()) {
if (!isset($options['mode']) || !in_array($options['mode'], array(self::MODE_CREATE_NEW, self::MODE_CREATE_ALL,
self::MODE_CREATE_OR_UPDATE, self::MODE_UPDATE_ONLY))) {
throw new coding_exception('Unknown process mode');
}
// Force int to make sure === comparison work as expected.
$this->mode = (int) $options['mode'];
$this->updatemode = self::UPDATE_NOTHING;
if (isset($options['updatemode'])) {
// Force int to make sure === comparison work as expected.
$this->updatemode = (int) $options['updatemode'];
}
if (isset($options['allowrenames'])) {
$this->allowrenames = $options['allowrenames'];
}
if (isset($options['allowdeletes'])) {
$this->allowdeletes = $options['allowdeletes'];
}
if (isset($options['allowresets'])) {
$this->allowresets = $options['allowresets'];
}
if (isset($options['restorefile'])) {
$this->restorefile = $options['restorefile'];
}
if (isset($options['templatecourse'])) {
$this->templatecourse = $options['templatecourse'];
}
if (isset($options['reset'])) {
$this->reset = $options['reset'];
}
if (isset($options['shortnametemplate'])) {
$this->shortnametemplate = $options['shortnametemplate'];
}
$this->cir = $cir;
$this->columns = $cir->get_columns();
$this->defaults = $defaults;
$this->validate();
$this->reset();
}
/**
* Execute the process.
*
* @param object $tracker the output tracker to use.
* @return void
*/
public function execute($tracker = null) {
if ($this->processstarted) {
throw new coding_exception('Process has already been started');
}
$this->processstarted = true;
if (empty($tracker)) {
$tracker = new tool_uploadcourse_tracker(tool_uploadcourse_tracker::NO_OUTPUT);
}
$tracker->start();
$total = 0;
$created = 0;
$updated = 0;
$deleted = 0;
$errors = 0;
// Loop over the CSV lines.
while ($line = $this->cir->next()) {
$this->linenb++;
$total++;
$data = $this->parse_line($line);
$course = $this->get_course($data);
if ($course->prepare()) {
$course->proceed();
$status = $course->get_statuses();
if (array_key_exists('coursecreated', $status)) {
$created++;
} else if (array_key_exists('courseupdated', $status)) {
$updated++;
} else if (array_key_exists('coursedeleted', $status)) {
$deleted++;
}
$data = array_merge($data, $course->get_data(), array('id' => $course->get_id()));
$tracker->output($this->linenb, true, $status, $data);
} else {
$errors++;
$tracker->output($this->linenb, false, $course->get_errors(), $data);
}
}
$tracker->finish();
$tracker->results($total, $created, $updated, $deleted, $errors);
$this->remove_restore_content();
}
/**
* Return a course import object.
*
* @param array $data data to import the course with.
* @return tool_uploadcourse_course
*/
protected function get_course($data) {
$importoptions = array(
'candelete' => $this->allowdeletes,
'canrename' => $this->allowrenames,
'canreset' => $this->allowresets,
'reset' => $this->reset,
'restoredir' => $this->get_restore_content_dir(),
'shortnametemplate' => $this->shortnametemplate
);
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.
*
* @return string subdirectory in $CFG->tempdir/backup/...
*/
protected function get_restore_content_dir() {
$backupfile = null;
$shortname = null;
if (!empty($this->restorefile)) {
$backupfile = $this->restorefile;
} else if (!empty($this->templatecourse) || is_numeric($this->templatecourse)) {
$shortname = $this->templatecourse;
}
$dir = tool_uploadcourse_helper::get_restore_content_dir($backupfile, $shortname);
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)
*
* @param array $line returned by csv_import_reader
* @return array
*/
protected function parse_line($line) {
$data = array();
foreach ($line as $keynum => $value) {
if (!isset($this->columns[$keynum])) {
// This should not happen.
continue;
}
$key = $this->columns[$keynum];
$data[$key] = $value;
}
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.
* @param object $tracker the output tracker to use.
* @return array of preview data.
*/
public function preview($rows = 10, $tracker = null) {
if ($this->processstarted) {
throw new coding_exception('Process has already been started');
}
$this->processstarted = true;
if (empty($tracker)) {
$tracker = new tool_uploadcourse_tracker(tool_uploadcourse_tracker::NO_OUTPUT);
}
$tracker->start();
// 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) {
$tracker->output($this->linenb, $result, $course->get_errors(), $data);
} else {
$tracker->output($this->linenb, $result, $course->get_statuses(), $data);
}
$row = $data;
$preview[$this->linenb] = $row;
}
$tracker->finish();
$this->remove_restore_content();
return $preview;
}
/**
* Delete the restore object.
*
* @return void
*/
protected function remove_restore_content() {
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');
}
}
}
@@ -0,0 +1,75 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* File containing the step 1 of the upload form.
*
* @package tool_uploadcourse
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir.'/formslib.php');
/**
* Upload a file CVS file with course information.
*
* @package tool_uploadcourse
* @copyright 2011 Piers Harding
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class tool_uploadcourse_step1_form extends tool_uploadcourse_base_form {
/**
* The standard form definiton.
* @return void
*/
public function definition () {
$mform = $this->_form;
$mform->addElement('header', 'generalhdr', get_string('general'));
$mform->addElement('filepicker', 'coursefile', get_string('file'));
$mform->addRule('coursefile', null, 'required');
$choices = csv_import_reader::get_delimiter_list();
$mform->addElement('select', 'delimiter_name', get_string('csvdelimiter', 'tool_uploadcourse'), $choices);
if (array_key_exists('cfg', $choices)) {
$mform->setDefault('delimiter_name', 'cfg');
} else if (get_string('listsep', 'langconfig') == ';') {
$mform->setDefault('delimiter_name', 'semicolon');
} else {
$mform->setDefault('delimiter_name', 'comma');
}
$choices = textlib::get_encodings();
$mform->addElement('select', 'encoding', get_string('encoding', 'tool_uploadcourse'), $choices);
$mform->setDefault('encoding', 'UTF-8');
$choices = array('10' => 10, '20' => 20, '100' => 100, '1000' => 1000, '100000' => 100000);
$mform->addElement('select', 'previewrows', get_string('rowpreviewnum', 'tool_uploadcourse'), $choices);
$mform->setType('previewrows', PARAM_INT);
$this->add_import_options();
$mform->addElement('hidden', 'showpreview', 1);
$mform->setType('showpreview', PARAM_INT);
$this->add_action_buttons(false, get_string('preview', 'tool_uploadcourse'));
}
}
@@ -0,0 +1,220 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Bulk course upload step 2.
*
* @package tool_uploadcourse
* @copyright 2011 Piers Harding
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Specify course upload details.
*
* @package tool_uploadcourse
* @copyright 2011 Piers Harding
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class tool_uploadcourse_step2_form extends tool_uploadcourse_base_form {
/**
* The standard form definiton.
* @return void.
*/
public function definition () {
global $CFG;
$mform = $this->_form;
$data = $this->_customdata['data'];
$courseconfig = get_config('moodlecourse');
// Import options.
$this->add_import_options();
// Course options.
$mform->addElement('header', 'courseoptionshdr', get_string('courseprocess', 'tool_uploadcourse'));
$mform->setExpanded('courseoptionshdr', true);
$mform->addElement('text', 'options[shortnametemplate]', get_string('shortnametemplate', 'tool_uploadcourse'),
'maxlength="100" size="20"');
$mform->setType('options[shortnametemplate]', PARAM_RAW);
$mform->addHelpButton('options[shortnametemplate]', 'shortnametemplate', 'tool_uploadcourse');
$mform->disabledIf('options[shortnametemplate]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE);
$mform->disabledIf('options[shortnametemplate]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_UPDATE_ONLY);
$contextid = $this->_customdata['contextid'];
$mform->addElement('hidden', 'contextid', $contextid);
$mform->setType('contextid', PARAM_INT);
$mform->addElement('filepicker', 'options[restorefile]', get_string('templatefile', 'tool_uploadcourse'));
$mform->addHelpButton('options[restorefile]', 'templatefile', 'tool_uploadcourse');
$mform->addElement('text', 'options[templatecourse]', get_string('coursetemplatename', 'tool_uploadcourse'));
$mform->setType('options[templatecourse]', PARAM_TEXT);
$mform->addHelpButton('options[templatecourse]', 'coursetemplatename', 'tool_uploadcourse');
$mform->addElement('selectyesno', 'options[reset]', get_string('reset', 'tool_uploadcourse'));
$mform->setDefault('options[reset]', 0);
$mform->disabledIf('options[reset]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_NEW);
$mform->disabledIf('options[reset]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_ALL);
$mform->disabledIf('options[reset]', 'options[allowresets]', 'eq', 0);
// Default values.
$mform->addElement('header', 'defaultheader', get_string('defaultvalues', 'tool_uploadcourse'));
$mform->setExpanded('defaultheader', true);
$displaylist = coursecat::make_categories_list('moodle/course:create');
$mform->addElement('select', 'defaults[category]', get_string('coursecategory'), $displaylist);
$mform->addHelpButton('defaults[category]', 'coursecategory');
$choices = array();
$choices['0'] = get_string('hide');
$choices['1'] = get_string('show');
$mform->addElement('select', 'defaults[visible]', get_string('visible'), $choices);
$mform->addHelpButton('defaults[visible]', 'visible');
$mform->setDefault('defaults[visible]', $courseconfig->visible);
$mform->addElement('date_selector', 'defaults[startdate]', get_string('startdate'));
$mform->addHelpButton('defaults[startdate]', 'startdate');
$mform->setDefault('defaults[startdate]', time() + 3600 * 24);
$courseformats = get_sorted_course_formats(true);
$formcourseformats = array();
foreach ($courseformats as $courseformat) {
$formcourseformats[$courseformat] = get_string('pluginname', "format_$courseformat");
}
$mform->addElement('select', 'defaults[format]', get_string('format'), $formcourseformats);
$mform->addHelpButton('defaults[format]', 'format');
$mform->setDefault('defaults[format]', $courseconfig->format);
if (!empty($CFG->allowcoursethemes)) {
$themeobjects = get_list_of_themes();
$themes=array();
$themes[''] = get_string('forceno');
foreach ($themeobjects as $key => $theme) {
if (empty($theme->hidefromselector)) {
$themes[$key] = get_string('pluginname', 'theme_'.$theme->name);
}
}
$mform->addElement('select', 'defaults[theme]', get_string('forcetheme'), $themes);
}
$languages = array();
$languages[''] = get_string('forceno');
$languages += get_string_manager()->get_list_of_translations();
$mform->addElement('select', 'defaults[lang]', get_string('forcelanguage'), $languages);
$mform->setDefault('defaults[lang]', $courseconfig->lang);
$options = range(0, 10);
$mform->addElement('select', 'defaults[newsitems]', get_string('newsitemsnumber'), $options);
$mform->addHelpButton('defaults[newsitems]', 'newsitemsnumber');
$mform->setDefault('defaults[newsitems]', $courseconfig->newsitems);
$mform->addElement('selectyesno', 'defaults[showgrades]', get_string('showgrades'));
$mform->addHelpButton('defaults[showgrades]', 'showgrades');
$mform->setDefault('defaults[showgrades]', $courseconfig->showgrades);
$mform->addElement('selectyesno', 'defaults[showreports]', get_string('showreports'));
$mform->addHelpButton('defaults[showreports]', 'showreports');
$mform->setDefault('defaults[showreports]', $courseconfig->showreports);
if (!empty($CFG->legacyfilesinnewcourses)) {
$mform->addElement('select', 'defaults[legacyfiles]', get_string('courselegacyfiles'), $choices);
$mform->addHelpButton('defaults[legacyfiles]', 'courselegacyfiles');
if (!isset($courseconfig->legacyfiles)) {
$courseconfig->legacyfiles = 0;
}
$mform->setDefault('defaults[legacyfiles]', $courseconfig->legacyfiles);
}
$choices = get_max_upload_sizes($CFG->maxbytes);
$mform->addElement('select', 'defaults[maxbytes]', get_string('maximumupload'), $choices);
$mform->addHelpButton('defaults[maxbytes]', 'maximumupload');
$mform->setDefault('defaults[maxbytes]', $courseconfig->maxbytes);
$choices = array();
$choices[NOGROUPS] = get_string('groupsnone', 'group');
$choices[SEPARATEGROUPS] = get_string('groupsseparate', 'group');
$choices[VISIBLEGROUPS] = get_string('groupsvisible', 'group');
$mform->addElement('select', 'defaults[groupmode]', get_string('groupmode', 'group'), $choices);
$mform->addHelpButton('defaults[groupmode]', 'groupmode', 'group');
$mform->setDefault('defaults[groupmode]', $courseconfig->groupmode);
$mform->addElement('selectyesno', 'defaults[groupmodeforce]', get_string('groupmodeforce', 'group'));
$mform->addHelpButton('defaults[groupmodeforce]', 'groupmodeforce', 'group');
$mform->setDefault('defaults[groupmodeforce]', $courseconfig->groupmodeforce);
// Hidden fields.
$mform->addElement('hidden', 'importid');
$mform->setType('importid', PARAM_INT);
$mform->addElement('hidden', 'previewrows');
$mform->setType('previewrows', PARAM_INT);
$this->add_action_buttons(true, get_string('uploadcourses', 'tool_uploadcourse'));
$this->set_data($data);
}
/**
* Add actopm buttons.
*
* @param bool $cancel whether to show cancel button, default true
* @param string $submitlabel label for submit button, defaults to get_string('savechanges')
* @return void
*/
public function add_action_buttons($cancel = true, $submitlabel = null) {
$mform =& $this->_form;
$buttonarray = array();
$buttonarray[] = &$mform->createElement('submit', 'showpreview', get_string('preview', 'tool_uploadcourse'));
$buttonarray[] = &$mform->createElement('submit', 'submitbutton', $submitlabel);
$buttonarray[] = &$mform->createElement('cancel');
$mform->addGroup($buttonarray, 'buttonar', '', array(' '), false);
$mform->closeHeaderBefore('buttonar');
}
/**
* Server side validation.
* @param array $data - form data
* @param object $files - form files
* @return array $errors - form errors
*/
public function validation($data, $files) {
$errors = parent::validation($data, $files);
$columns = $this->_customdata['columns'];
$optype = $data['options']['mode'];
// Look for other required data.
if ($optype != tool_uploadcourse_processor::MODE_UPDATE_ONLY) {
if (!in_array('fullname', $columns)) {
if (isset($errors['mode'])) {
$errors['mode'] .= ' ';
}
$errors['mode'] .= get_string('missingfield', 'error', 'fullname');
}
if (!in_array('summary', $columns)) {
if (isset($errors['mode'])) {
$errors['mode'] .= ' ';
}
$errors['mode'] .= get_string('missingfield', 'error', 'summary');
}
}
return $errors;
}
}
+220
View File
@@ -0,0 +1,220 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Output tracker.
*
* @package tool_uploadcourse
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/weblib.php');
/**
* Class output tracker.
*
* @package tool_uploadcourse
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class tool_uploadcourse_tracker {
/**
* Constant to output nothing.
*/
const NO_OUTPUT = 0;
/**
* Constant to output HTML.
*/
const OUTPUT_HTML = 1;
/**
* Constant to output plain text.
*/
const OUTPUT_PLAIN = 2;
/**
* @var array columns to display.
*/
protected $columns = array('line', 'result', 'id', 'shortname', 'fullname', 'idnumber', 'status');
/**
* @var int row number.
*/
protected $rownb = 0;
/**
* @var int chosen output mode.
*/
protected $outputmode;
/**
* @var object output buffer.
*/
protected $buffer;
/**
* Constructor.
*
* @param int $outputmode desired output mode.
*/
public function __construct($outputmode = self::NO_OUTPUT) {
$this->outputmode = $outputmode;
if ($this->outputmode == self::OUTPUT_PLAIN) {
$this->buffer = new progress_trace_buffer(new text_progress_trace());
}
}
/**
* Finish the output.
*
* @return void
*/
public function finish() {
if ($this->outputmode == self::NO_OUTPUT) {
return;
}
if ($this->outputmode == self::OUTPUT_HTML) {
echo html_writer::end_tag('table');
}
}
/**
* Output the results.
*
* @param int $total total courses.
* @param int $created count of courses created.
* @param int $updated count of courses updated.
* @param int $deleted count of courses deleted.
* @param int $errors count of errors.
* @return void
*/
public function results($total, $created, $updated, $deleted, $errors) {
if ($this->outputmode == self::NO_OUTPUT) {
return;
}
$message = array(
get_string('coursestotal', 'tool_uploadcourse', $total),
get_string('coursescreated', 'tool_uploadcourse', $created),
get_string('coursesupdated', 'tool_uploadcourse', $updated),
get_string('coursesdeleted', 'tool_uploadcourse', $deleted),
get_string('courseserrors', 'tool_uploadcourse', $errors)
);
if ($this->outputmode == self::OUTPUT_PLAIN) {
foreach ($message as $msg) {
$this->buffer->output($msg);
}
} else if ($this->outputmode == self::OUTPUT_HTML) {
$buffer = new progress_trace_buffer(new html_list_progress_trace());
foreach ($message as $msg) {
$buffer->output($msg);
}
$buffer->finished();
}
}
/**
* Output one more line.
*
* @param int $line line number.
* @param bool $outcome success or not?
* @param array $status array of statuses.
* @param array $data extra data to display.
* @return void
*/
public function output($line, $outcome, $status, $data) {
global $OUTPUT;
if ($this->outputmode == self::NO_OUTPUT) {
return;
}
if ($this->outputmode == self::OUTPUT_PLAIN) {
$message = array(
$line,
$outcome ? 'OK' : 'NOK',
isset($data['id']) ? $data['id'] : '',
isset($data['shortname']) ? $data['shortname'] : '',
isset($data['fullname']) ? $data['fullname'] : '',
isset($data['idnumber']) ? $data['idnumber'] : ''
);
$this->buffer->output(implode("\t", $message));
if (!empty($status)) {
foreach ($status as $st) {
$this->buffer->output($st, 1);
}
}
} else if ($this->outputmode == self::OUTPUT_HTML) {
$ci = 0;
$this->rownb++;
if (is_array($status)) {
$status = implode(html_writer::empty_tag('br'), $status);
}
if ($outcome) {
$outcome = $OUTPUT->pix_icon('i/valid', '');
} else {
$outcome = $OUTPUT->pix_icon('i/invalid', '');
}
echo html_writer::start_tag('tr', array('class' => 'r' . $this->rownb % 2));
echo html_writer::tag('td', $line, array('class' => 'c' . $ci++));
echo html_writer::tag('td', $outcome, array('class' => 'c' . $ci++));
echo html_writer::tag('td', isset($data['id']) ? $data['id'] : '', array('class' => 'c' . $ci++));
echo html_writer::tag('td', isset($data['shortname']) ? $data['shortname'] : '', array('class' => 'c' . $ci++));
echo html_writer::tag('td', isset($data['fullname']) ? $data['fullname'] : '', array('class' => 'c' . $ci++));
echo html_writer::tag('td', isset($data['idnumber']) ? $data['idnumber'] : '', array('class' => 'c' . $ci++));
echo html_writer::tag('td', $status, array('class' => 'c' . $ci++));
echo html_writer::end_tag('tr');
}
}
/**
* Start the output.
*
* @return void
*/
public function start() {
if ($this->outputmode == self::NO_OUTPUT) {
return;
}
if ($this->outputmode == self::OUTPUT_PLAIN) {
$columns = array_flip($this->columns);
unset($columns['status']);
$columns = array_flip($columns);
$this->buffer->output(implode("\t", $columns));
} else if ($this->outputmode == self::OUTPUT_HTML) {
$ci = 0;
echo html_writer::start_tag('table', array('class' => 'generaltable boxaligncenter flexible-wrap',
'summary' => get_string('uploadcoursesresult', 'tool_uploadcourse')));
echo html_writer::start_tag('tr', array('class' => 'heading r' . $this->rownb));
echo html_writer::tag('th', get_string('csvline', 'tool_uploadcourse'),
array('class' => 'c' . $ci++, 'scope' => 'col'));
echo html_writer::tag('th', get_string('result', 'tool_uploadcourse'), array('class' => 'c' . $ci++, 'scope' => 'col'));
echo html_writer::tag('th', get_string('id', 'tool_uploadcourse'), array('class' => 'c' . $ci++, 'scope' => 'col'));
echo html_writer::tag('th', get_string('shortname'), array('class' => 'c' . $ci++, 'scope' => 'col'));
echo html_writer::tag('th', get_string('fullname'), array('class' => 'c' . $ci++, 'scope' => 'col'));
echo html_writer::tag('th', get_string('idnumber'), array('class' => 'c' . $ci++, 'scope' => 'col'));
echo html_writer::tag('th', get_string('status'), array('class' => 'c' . $ci++, 'scope' => 'col'));
echo html_writer::end_tag('tr');
}
}
}
@@ -0,0 +1,200 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* CLI Bulk course registration script from a comma separated file.
*
* @package tool_uploadcourse
* @copyright 2012 Piers Harding
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', true);
require(__DIR__ . '/../../../../config.php');
require_once($CFG->libdir . '/clilib.php');
require_once($CFG->libdir . '/coursecatlib.php');
require_once($CFG->libdir . '/csvlib.class.php');
$courseconfig = get_config('moodlecourse');
// Now get cli options.
list($options, $unrecognized) = cli_get_params(array(
'help' => false,
'mode' => '',
'updatemode' => 'nothing',
'file' => '',
'delimiter' => 'comma',
'encoding' => 'UTF-8',
'shortnametemplate' => '',
'templatecourse' => false,
'restorefile' => false,
'allowdeletes' => false,
'allowrenames' => false,
'allowresets' => false,
'reset' => false,
'category' => coursecat::get_default()->id,
),
array(
'h' => 'help',
'm' => 'mode',
'u' => 'updatemode',
'f' => 'file',
'd' => 'delimiter',
'e' => 'encoding',
't' => 'templatecourse',
'r' => 'restorefile',
'g' => 'format',
));
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
}
$help =
"Execute Course Upload.
Options:
-h, --help Print out this help
-m, --mode Import mode: createnew, createall, createorupdate, update
-u, --updatemode Update mode: nothing, dataonly, dataordefaults¸ missingonly
-f, --file CSV file
-d, --delimiter CSV delimiter: colon, semicolon, tab, cfg, comma
-e, --encoding CSV file encoding: utf8, ... etc
-t, --templatecourse Shortname of the course to restore after import
-r, --restorefile Backup file to restore after import
--reset Run the course reset after each course import
--allowdeletes Allow courses to be deleted
--allowrenames Allow courses to be renamed
--allowresets Allow courses to be reset
--shortnametemplate Template to generate the shortname from
--category ID of default category (--updatemode dataordefaults will use this value)
Example:
\$sudo -u www-data /usr/bin/php admin/tool/uploadcourse/cli/uploadcourse.php --action=createnew \\
--updatemode=dataonly --file=./courses.csv --delimiter=comma
";
if ($options['help']) {
echo $help;
die();
}
echo "Moodle course uploader running ...\n";
$processoroptions = array(
'allowdeletes' => $options['allowdeletes'],
'allowrenames' => $options['allowrenames'],
'allowresets' => $options['allowresets'],
'reset' => $options['reset'],
'shortnametemplate' => $options['shortnametemplate']
);
// Confirm that the mode is valid.
$modes = array(
'createnew' => tool_uploadcourse_processor::MODE_CREATE_NEW,
'createall' => tool_uploadcourse_processor::MODE_CREATE_ALL,
'createorupdate' => tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE,
'update' => tool_uploadcourse_processor::MODE_UPDATE_ONLY
);
if (!isset($options['mode']) || !isset($modes[$options['mode']])) {
echo get_string('invalidmode', 'tool_uploadcourse')."\n";
echo $help;
die();
}
$processoroptions['mode'] = $modes[$options['mode']];
// Check that the update mode is valid.
$updatemodes = array(
'nothing' => tool_uploadcourse_processor::UPDATE_NOTHING,
'dataonly' => tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY,
'dataordefaults' => tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_OR_DEFAUTLS,
'missingonly' => tool_uploadcourse_processor::UPDATE_MISSING_WITH_DATA_OR_DEFAUTLS
);
if (($processoroptions['mode'] === tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE ||
$processoroptions['mode'] === tool_uploadcourse_processor::MODE_UPDATE_ONLY)
&& (!isset($options['updatemode']) || !isset($updatemodes[$options['updatemode']]))) {
echo get_string('invalideupdatemode', 'tool_uploadcourse')."\n";
echo $help;
die();
}
$processoroptions['updatemode'] = $updatemodes[$options['updatemode']];
// File.
if (!empty($options['file'])) {
$options['file'] = realpath($options['file']);
}
if (!file_exists($options['file'])) {
echo get_string('invalidcsvfile', 'tool_uploadcourse')."\n";
echo $help;
die();
}
// Encoding.
$encodings = textlib::get_encodings();
if (!isset($encodings[$options['encoding']])) {
echo get_string('invalidencoding', 'tool_uploadcourse')."\n";
echo $help;
die();
}
// Default values.
$defaults = array();
$defaults['category'] = $options['category'];
$defaults['startdate'] = time() + 3600 * 24;
$defaults['newsitems'] = $courseconfig->newsitems;
$defaults['showgrades'] = $courseconfig->showgrades;
$defaults['showreports'] = $courseconfig->showreports;
$defaults['maxbytes'] = $courseconfig->maxbytes;
$defaults['legacyfiles'] = $CFG->legacyfilesinnewcourses;
$defaults['groupmode'] = $courseconfig->groupmode;
$defaults['groupmodeforce'] = $courseconfig->groupmodeforce;
$defaults['visible'] = $courseconfig->visible;
$defaults['lang'] = $courseconfig->lang;
// Course template.
if (isset($options['templatecourse'])) {
$processoroptions['templatecourse'] = $options['templatecourse'];
}
// Restore file.
if ($options['restorefile']) {
$options['restorefile'] = realpath($options['restorefile']);
}
if ($options['restorefile'] && !file_exists($options['restorefile'])) {
echo get_string('invalidrestorefile', 'tool_uploadcourse')."\n";
echo $help;
die();
}
$processoroptions['restorefile'] = $options['restorefile'];
// Emulate normal session.
cron_setup_user();
// Let's get started!
$content = file_get_contents($options['file']);
$importid = csv_import_reader::get_new_iid('uploadcourse');
$cir = new csv_import_reader($importid, 'uploadcourse');
$readcount = $cir->load_csv_content($content, $options['encoding'], $options['delimiter']);
unset($content);
if ($readcount === false) {
print_error('csvfileerror', 'tool_uploadcourse', '', $cir->get_error());
} else if ($readcount == 0) {
print_error('csvemptyfile', 'error', '', $cir->get_error());
}
$processor = new tool_uploadcourse_processor($cir, $processoroptions, $defaults);
$processor->execute(new tool_uploadcourse_tracker(tool_uploadcourse_tracker::OUTPUT_PLAIN));
+31
View File
@@ -0,0 +1,31 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Cache definitions.
*
* @package tool_uploadcourse
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$definitions = array(
'helper' => array(
'mode' => cache_store::MODE_REQUEST,
)
);
+102
View File
@@ -0,0 +1,102 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Bulk course registration script from a comma separated file.
*
* @package tool_uploadcourse
* @copyright 2011 Piers Harding
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require(__DIR__ . '/../../../config.php');
require_once($CFG->libdir . '/adminlib.php');
require_once($CFG->libdir . '/coursecatlib.php');
require_once($CFG->libdir . '/csvlib.class.php');
admin_externalpage_setup('tooluploadcourse');
$importid = optional_param('importid', '', PARAM_INT);
$previewrows = optional_param('previewrows', 10, PARAM_INT);
$returnurl = new moodle_url('/admin/tool/uploadcourse/index.php');
if (empty($importid)) {
$mform1 = new tool_uploadcourse_step1_form();
if ($form1data = $mform1->get_data()) {
$importid = csv_import_reader::get_new_iid('uploadcourse');
$cir = new csv_import_reader($importid, 'uploadcourse');
$content = $mform1->get_file_content('coursefile');
$readcount = $cir->load_csv_content($content, $form1data->encoding, $form1data->delimiter_name);
unset($content);
if ($readcount === false) {
print_error('csvfileerror', 'tool_uploadcourse', $returnurl, $cir->get_error());
} else if ($readcount == 0) {
print_error('csvemptyfile', 'error', $returnurl, $cir->get_error());
}
} else {
echo $OUTPUT->header();
echo $OUTPUT->heading_with_help(get_string('uploadcourses', 'tool_uploadcourse'), 'uploadcourses', 'tool_uploadcourse');
$mform1->display();
echo $OUTPUT->footer();
die();
}
} else {
$cir = new csv_import_reader($importid, 'uploadcourse');
}
// Data to set in the form.
$data = array('importid' => $importid, 'previewrows' => $previewrows);
if (!empty($form1data)) {
// Get options from the first form to pass it onto the second.
foreach ($form1data->options as $key => $value) {
$data["options[$key]"] = $value;
}
}
$context = context_system::instance();
$mform2 = new tool_uploadcourse_step2_form(null, array('contextid' => $context->id, 'columns' => $cir->get_columns(),
'data' => $data));
// If a file has been uploaded, then process it.
if ($form2data = $mform2->is_cancelled()) {
$cir->cleanup(true);
redirect($returnurl);
} else if ($form2data = $mform2->get_data()) {
$options = (array) $form2data->options;
$defaults = (array) $form2data->defaults;
$processor = new tool_uploadcourse_processor($cir, $options, $defaults);
echo $OUTPUT->header();
if (isset($form2data->showpreview)) {
echo $OUTPUT->heading(get_string('uploadcoursespreview', 'tool_uploadcourse'));
$processor->preview($previewrows, new tool_uploadcourse_tracker(tool_uploadcourse_tracker::OUTPUT_HTML));
$mform2->display();
} else {
echo $OUTPUT->heading(get_string('uploadcoursesresult', 'tool_uploadcourse'));
$processor->execute(new tool_uploadcourse_tracker(tool_uploadcourse_tracker::OUTPUT_HTML));
echo $OUTPUT->continue_button($returnurl);
}
} else {
$processor = new tool_uploadcourse_processor($cir, $form1data->options, array());
echo $OUTPUT->header();
echo $OUTPUT->heading(get_string('uploadcoursespreview', 'tool_uploadcourse'));
$processor->preview($previewrows, new tool_uploadcourse_tracker(tool_uploadcourse_tracker::OUTPUT_HTML));
$mform2->display();
}
echo $OUTPUT->footer();
@@ -0,0 +1,115 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Strings for component 'tool_uploadcourse'.
*
* @package tool_uploadcourse
* @copyright 2011 Piers Harding
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['allowdeletes'] = 'Allow deletes';
$string['allowrenames'] = 'Allow renames';
$string['allowresets'] = 'Allow resets';
$string['cachedef_helper'] = 'Helper caching';
$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 is not allowed';
$string['courseidnumberincremented'] = 'Course ID number incremented {$a->from} -> {$a->to}';
$string['courseprocess'] = 'Course process';
$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['coursestotal'] = 'Courses total: {$a}';
$string['coursescreated'] = 'Courses created: {$a}';
$string['coursesupdated'] = 'Courses updated: {$a}';
$string['coursesdeleted'] = 'Courses deleted: {$a}';
$string['courseserrors'] = 'Courses errors: {$a}';
$string['courseshortnameincremented'] = 'Course shortname incremented {$a->from} -> {$a->to}';
$string['courseshortnamegenerated'] = 'Course shortname generated: {$a}';
$string['coursetemplatename'] = 'Restore from this course after upload';
$string['coursetemplatename_help'] = 'Enter an existing course shortname to use as a template for the creation of all courses.';
$string['coursetorestorefromdoesnotexist'] = 'The course to restore from does not exist';
$string['courseupdated'] = 'Course updated';
$string['createall'] = 'Create all, increment shortname if needed';
$string['createnew'] = 'Create new courses only, skip existing ones';
$string['createorupdate'] = 'Create new courses, or update existing ones';
$string['csvdelimiter'] = 'CSV delimiter';
$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: {$a}';
$string['csvline'] = 'Line';
$string['defaultvalues'] = 'Default course values';
$string['encoding'] = 'Encoding';
$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['id'] = 'ID';
$string['importoptions'] = 'Import options';
$string['idnumberalreadyinuse'] = 'ID number already used by a course';
$string['invalidbackupfile'] = 'Invalid backup file';
$string['invalidcourseformat'] = 'Invalid course format';
$string['invalidcsvfile'] = 'Invalid input CSV file';
$string['invalidencoding'] = 'Invalid encoding';
$string['invalidmode'] = 'Invalid mode selected';
$string['invalideupdatemode'] = 'Invalid update mode selected';
$string['invalidroles'] = 'Invalid role names: {$a}';
$string['invalidshortname'] = 'Invalid shortname';
$string['missingmandatoryfields'] = 'Missing value for mandatory fields: {$a}';
$string['missingshortnamenotemplate'] = 'Missing shortname and shortname template not set';
$string['mode'] = 'Upload mode';
$string['nochanges'] = 'No changes';
$string['pluginname'] = 'Course upload';
$string['preview'] = 'Preview';
$string['reset'] = 'Reset course after upload';
$string['result'] = 'Result';
$string['restoreafterimport'] = 'Restore after import';
$string['rowpreviewnum'] = 'Preview rows';
$string['shortnametemplate'] = 'Template to generate a shortname';
$string['shortnametemplate_help'] = 'The short name of the course is displayed in the navigation. You may use template syntax here (%f = fullname, %i = idnumber), or enter an initial value that is incremented.';
$string['templatefile'] = 'Restore from this file after upload';
$string['templatefile_help'] = 'Select a file to use as a template for the creation of all courses.';
$string['unknownimportmode'] = 'Unknown import mode';
$string['updatemissing'] = 'Fill in missing from CSV data and defaults';
$string['updatemode'] = 'Update mode';
$string['updatemodedoessettonothing'] = 'Update mode does not allow anything to be updated';
$string['updateonly'] = 'Only update existing courses';
$string['updatewithdataordefaults'] = 'Update with CSV data and defaults';
$string['updatewithdataonly'] = 'Update with CSV data only';
$string['uploadcourses'] = 'Upload courses';
$string['uploadcourses_help'] = 'Courses may be uploaded via text file. The format of the file should be as follows:
* Each line of the file contains one record
* Each record is a series of data separated by commas (or other delimiters)
* The first record contains a list of fieldnames defining the format of the rest of the file
* Required fieldnames are shortname, fullname, summary and category';
$string['uploadcoursespreview'] = 'Upload courses preview';
$string['uploadcoursesresult'] = 'Upload courses results';
+30
View File
@@ -0,0 +1,30 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Link to CSV course upload.
*
* @package tool_uploadcourse
* @copyright 2011 Piers Harding
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
if ($hassiteconfig) {
$ADMIN->add('courses', new admin_externalpage('tooluploadcourse',
get_string('uploadcourses', 'tool_uploadcourse'), "/admin/tool/uploadcourse/index.php"));
}
@@ -0,0 +1,46 @@
@tool @tool_uploadcourse @_only_local
Feature: An admin can create courses using a CSV file
In order to create courses using a CSV file
As an admin
I need to be able to upload a CSV file and navigate through the import process
Background:
Given the following "courses" exists:
| fullname | shortname | category |
| First course | C1 | 0 |
And I log in as "admin"
And I expand "Site administration" node
And I expand "Courses" node
And I follow "Upload courses"
@javascript
Scenario: Creation of unexisting courses
Given I upload "admin/tool/uploadcourse/tests/fixtures/courses.csv" file to "File" filepicker
And I click on "Preview" "button"
When I click on "Upload courses" "button"
Then I should see "The course exists and update is not allowed"
And I should see "Course created"
And I should see "Courses total: 3"
And I should see "Courses created: 2"
And I should see "Courses errors: 1"
And I follow "Home"
And I should see "Course 2"
And I should see "Course 3"
@javascript
Scenario: Creation of existing courses
Given I upload "admin/tool/uploadcourse/tests/fixtures/courses.csv" file to "File" filepicker
And I select "Create all, increment shortname if needed" from "Upload mode"
And I click on "Preview" "button"
When I click on "Upload courses" "button"
Then I should see "Course created"
And I should see "Course shortname incremented C1 -> C2"
And I should see "Course shortname incremented C2 -> C3"
And I should see "Course shortname incremented C3 -> C4"
And I should see "Courses total: 3"
And I should see "Courses created: 3"
And I should see "Courses errors: 0"
And I follow "Home"
And I should see "Course 1"
And I should see "Course 2"
And I should see "Course 3"
@@ -0,0 +1,32 @@
@tool @tool_uploadcourse @_only_local
Feature: An admin can update courses using a CSV file
In order to update courses using a CSV file
As an admin
I need to be able to upload a CSV file and navigate through the import process
Background:
Given the following "courses" exists:
| fullname | shortname | category |
| Some random name | C1 | 0 |
And I log in as "admin"
And I expand "Site administration" node
And I expand "Courses" node
And I follow "Upload courses"
@javascript
Scenario: Updating a course fullname
Given I upload "admin/tool/uploadcourse/tests/fixtures/courses.csv" file to "File" filepicker
And I select "Only update existing courses" from "Upload mode"
And I select "Update with CSV data only" from "Update mode"
And I click on "Preview" "button"
When I click on "Upload courses" "button"
Then I should see "Course updated"
And I should see "The course does not exist and creating course is not allowed"
And I should see "Courses total: 3"
And I should see "Courses updated: 1"
And I should see "Courses created: 0"
And I should see "Courses errors: 2"
And I follow "Home"
And I should see "Course 1"
And I should not see "Course 2"
And I should not see "Course 3"
@@ -0,0 +1,923 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* File containing tests for the course class.
*
* @package tool_uploadcourse
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
/**
* Course test case.
*
* @package tool_uploadcourse
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or late
*/
class tool_uploadcourse_course_testcase extends advanced_testcase {
public function test_proceed_without_prepare() {
$this->resetAfterTest(true);
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
$updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
$data = array();
$co = new tool_uploadcourse_course($mode, $updatemode, $data);
$this->setExpectedException('coding_exception');
$co->proceed();
}
public function test_proceed_when_prepare_failed() {
$this->resetAfterTest(true);
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
$updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
$data = array();
$co = new tool_uploadcourse_course($mode, $updatemode, $data);
$this->assertFalse($co->prepare());
$this->setExpectedException('moodle_exception');
$co->proceed();
}
public function test_proceed_when_already_started() {
$this->resetAfterTest(true);
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
$updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
$data = array('shortname' => 'test', 'fullname' => 'New course', 'summary' => 'New', 'category' => 1);
$co = new tool_uploadcourse_course($mode, $updatemode, $data);
$this->assertTrue($co->prepare());
$co->proceed();
$this->setExpectedException('coding_exception');
$co->proceed();
}
public function test_invalid_shortname() {
$this->resetAfterTest(true);
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
$updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
$data = array('shortname' => '<invalid>', 'fullname' => 'New course', 'summary' => 'New', 'category' => 1);
$co = new tool_uploadcourse_course($mode, $updatemode, $data);
$this->assertFalse($co->prepare());
$this->assertArrayHasKey('invalidshortname', $co->get_errors());
}
public function test_create() {
global $DB;
$this->resetAfterTest(true);
// Existing course.
$c1 = $this->getDataGenerator()->create_course(array('shortname' => 'c1', 'summary' => 'Yay!'));
$this->assertTrue($DB->record_exists('course', array('shortname' => 'c1')));
$updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
// Try to add a new course.
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
$data = array('shortname' => 'newcourse', 'fullname' => 'New course', 'summary' => 'New', 'category' => 1);
$co = new tool_uploadcourse_course($mode, $updatemode, $data);
$this->assertTrue($co->prepare());
$this->assertFalse($DB->record_exists('course', array('shortname' => 'newcourse')));
$co->proceed();
$this->assertTrue($DB->record_exists('course', array('shortname' => 'newcourse')));
// Try to add a new course, that already exists.
$coursecount = $DB->count_records('course', array());
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
$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')));
// Try to add new with shortname incrementation.
$mode = tool_uploadcourse_processor::MODE_CREATE_ALL;
$data = array('shortname' => 'c1', 'fullname' => 'C1FN', 'summary' => 'C1', 'category' => 1);
$co = new tool_uploadcourse_course($mode, $updatemode, $data);
$this->assertTrue($co->prepare());
$co->proceed();
$this->assertTrue($DB->record_exists('course', array('shortname' => 'c2')));
}
public function test_delete() {
global $DB;
$this->resetAfterTest(true);
$c1 = $this->getDataGenerator()->create_course();
$c2 = $this->getDataGenerator()->create_course();
$this->assertTrue($DB->record_exists('course', array('shortname' => $c1->shortname)));
$this->assertFalse($DB->record_exists('course', array('shortname' => 'DoesNotExist')));
$mode = tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE;
$updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
// Try delete when option not available.
$importoptions = array('candelete' => false);
$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.
$importoptions = array('candelete' => true);
$data = array('shortname' => $c1->shortname, 'delete' => 0);
$co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
$this->assertTrue($co->prepare());
$co->proceed();
$this->assertTrue($DB->record_exists('course', array('shortname' => $c1->shortname)));
// Try delete when requested.
$importoptions = array('candelete' => true);
$data = array('shortname' => $c1->shortname, 'delete' => 1);
$co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
$this->assertTrue($co->prepare());
$co->proceed();
$this->assertFalse($DB->record_exists('course', array('shortname' => $c1->shortname)));
$this->assertTrue($DB->record_exists('course', array('shortname' => $c2->shortname)));
// Try deleting non-existing record, this should not fail.
$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() {
global $DB;
$this->resetAfterTest(true);
$c1 = $this->getDataGenerator()->create_course(array('shortname' => 'c1'));
// 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 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->proceed();
$this->assertEquals('New fullname', $DB->get_field_select('course', 'fullname', 'shortname = :s', array('s' => 'c1')));
// Try a proper update with defaults.
$mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
$updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_OR_DEFAUTLS;
$data = array('shortname' => 'c1', 'fullname' => 'Another fullname');
$defaults = array('fullname' => 'Not this one', 'summary' => 'Awesome summary');
$co = new tool_uploadcourse_course($mode, $updatemode, $data, $defaults);
$this->assertTrue($co->prepare());
$co->proceed();
$this->assertEquals('Another fullname', $DB->get_field_select('course', 'fullname', 'shortname = :s', array('s' => 'c1')));
$this->assertEquals('Awesome summary', $DB->get_field_select('course', 'summary', 'shortname = :s', array('s' => 'c1')));
// Try a proper update missing only.
$mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
$updatemode = tool_uploadcourse_processor::UPDATE_MISSING_WITH_DATA_OR_DEFAUTLS;
$DB->set_field('course', 'summary', '', array('shortname' => 'c1'));
$this->assertEquals('', $DB->get_field_select('course', 'summary', 'shortname = :s', array('s' => 'c1')));
$data = array('shortname' => 'c1', 'summary' => 'Fill in summary');
$defaults = array('summary' => 'Do not use this summary');
$co = new tool_uploadcourse_course($mode, $updatemode, $data, $defaults);
$this->assertTrue($co->prepare());
$co->proceed();
$this->assertEquals('Fill in summary', $DB->get_field_select('course', 'summary', 'shortname = :s', array('s' => 'c1')));
// Try a proper update missing only using defaults.
$mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
$updatemode = tool_uploadcourse_processor::UPDATE_MISSING_WITH_DATA_OR_DEFAUTLS;
$DB->set_field('course', 'summary', '', array('shortname' => 'c1'));
$this->assertEquals('', $DB->get_field_select('course', 'summary', 'shortname = :s', array('s' => 'c1')));
$data = array('shortname' => 'c1');
$defaults = array('summary' => 'Use this summary');
$co = new tool_uploadcourse_course($mode, $updatemode, $data, $defaults);
$this->assertTrue($co->prepare());
$co->proceed();
$this->assertEquals('Use this summary', $DB->get_field_select('course', 'summary', 'shortname = :s', array('s' => 'c1')));
}
public function test_data_saved() {
global $DB;
$this->resetAfterTest(true);
// Create.
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
$updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
$data = array(
'shortname' => 'c1',
'fullname' => 'Fullname',
'category' => '1',
'visible' => '0',
'startdate' => '8 June 1990',
'idnumber' => '123abc',
'summary' => 'Summary',
'format' => 'weeks',
'theme' => 'afterburner',
'lang' => 'en',
'newsitems' => '7',
'showgrades' => '0',
'showreports' => '1',
'legacyfiles' => '1',
'maxbytes' => '1234',
'groupmode' => '2',
'groupmodeforce' => '1',
'enablecompletion' => '1',
'role_teacher' => 'Knight',
'role_manager' => 'Jedi',
'enrolment_1' => 'guest',
'enrolment_2' => 'self',
'enrolment_2_roleid' => '1',
'enrolment_3' => 'manual',
'enrolment_3_disable' => '1',
);
$this->assertFalse($DB->record_exists('course', array('shortname' => 'c1')));
$co = new tool_uploadcourse_course($mode, $updatemode, $data);
$this->assertTrue($co->prepare());
$co->proceed();
$this->assertTrue($DB->record_exists('course', array('shortname' => 'c1')));
$course = $DB->get_record('course', array('shortname' => 'c1'));
$ctx = context_course::instance($course->id);
$this->assertEquals($data['fullname'], $course->fullname);
$this->assertEquals($data['category'], $course->category);
$this->assertEquals($data['visible'], $course->visible);
$this->assertEquals(mktime(0, 0, 0, 6, 8, 1990), $course->startdate);
$this->assertEquals($data['idnumber'], $course->idnumber);
$this->assertEquals($data['summary'], $course->summary);
$this->assertEquals($data['format'], $course->format);
$this->assertEquals($data['theme'], $course->theme);
$this->assertEquals($data['lang'], $course->lang);
$this->assertEquals($data['newsitems'], $course->newsitems);
$this->assertEquals($data['showgrades'], $course->showgrades);
$this->assertEquals($data['showreports'], $course->showreports);
$this->assertEquals($data['legacyfiles'], $course->legacyfiles);
$this->assertEquals($data['maxbytes'], $course->maxbytes);
$this->assertEquals($data['groupmode'], $course->groupmode);
$this->assertEquals($data['groupmodeforce'], $course->groupmodeforce);
$this->assertEquals($data['enablecompletion'], $course->enablecompletion);
// Roles.
$roleids = array();
$roles = get_all_roles();
foreach ($roles as $role) {
$roleids[$role->shortname] = $role->id;
}
$this->assertEquals('Knight', $DB->get_field_select('role_names', 'name',
'roleid = :roleid AND contextid = :ctxid', array('ctxid' => $ctx->id, 'roleid' => $roleids['teacher'])));
$this->assertEquals('Jedi', $DB->get_field_select('role_names', 'name',
'roleid = :roleid AND contextid = :ctxid', array('ctxid' => $ctx->id, 'roleid' => $roleids['manager'])));
// Enrolment methods.
$enroldata = array();
$instances = enrol_get_instances($course->id, false);
$this->assertCount(3, $instances);
foreach ($instances as $instance) {
$enroldata[$instance->enrol] = $instance;
}
$this->assertNotEmpty($enroldata['guest']);
$this->assertEquals(ENROL_INSTANCE_ENABLED, $enroldata['guest']->status);
$this->assertNotEmpty($enroldata['self']);
$this->assertEquals($data['enrolment_2_roleid'], $enroldata['self']->roleid);
$this->assertEquals(ENROL_INSTANCE_ENABLED, $enroldata['self']->status);
$this->assertNotEmpty($enroldata['manual']);
$this->assertEquals(ENROL_INSTANCE_DISABLED, $enroldata['manual']->status);
// Update existing course.
$cat = $this->getDataGenerator()->create_category();
$mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
$updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
$data = array(
'shortname' => 'c1',
'fullname' => 'Fullname 2',
'category' => $cat->id,
'visible' => '1',
'startdate' => '11 June 1984',
'idnumber' => 'changeidn',
'summary' => 'Summary 2',
'format' => 'topics',
'theme' => 'clean',
'lang' => '',
'newsitems' => '2',
'showgrades' => '1',
'showreports' => '0',
'legacyfiles' => '0',
'maxbytes' => '4321',
'groupmode' => '1',
'groupmodeforce' => '0',
'enablecompletion' => '0',
'role_teacher' => 'Teacher',
'role_manager' => 'Manager',
'enrolment_1' => 'guest',
'enrolment_1_disable' => '1',
'enrolment_2' => 'self',
'enrolment_2_roleid' => '2',
'enrolment_3' => 'manual',
'enrolment_3_delete' => '1',
);
$this->assertTrue($DB->record_exists('course', array('shortname' => 'c1')));
$co = new tool_uploadcourse_course($mode, $updatemode, $data);
$this->assertTrue($co->prepare());
$co->proceed();
$course = $DB->get_record('course', array('shortname' => 'c1'));
$ctx = context_course::instance($course->id);
$this->assertEquals($data['fullname'], $course->fullname);
$this->assertEquals($data['category'], $course->category);
$this->assertEquals($data['visible'], $course->visible);
$this->assertEquals(mktime(0, 0, 0, 6, 11, 1984), $course->startdate);
$this->assertEquals($data['idnumber'], $course->idnumber);
$this->assertEquals($data['summary'], $course->summary);
$this->assertEquals($data['format'], $course->format);
$this->assertEquals($data['theme'], $course->theme);
$this->assertEquals($data['lang'], $course->lang);
$this->assertEquals($data['newsitems'], $course->newsitems);
$this->assertEquals($data['showgrades'], $course->showgrades);
$this->assertEquals($data['showreports'], $course->showreports);
$this->assertEquals($data['legacyfiles'], $course->legacyfiles);
$this->assertEquals($data['maxbytes'], $course->maxbytes);
$this->assertEquals($data['groupmode'], $course->groupmode);
$this->assertEquals($data['groupmodeforce'], $course->groupmodeforce);
$this->assertEquals($data['enablecompletion'], $course->enablecompletion);
// Roles.
$roleids = array();
$roles = get_all_roles();
foreach ($roles as $role) {
$roleids[$role->shortname] = $role->id;
}
$this->assertEquals('Teacher', $DB->get_field_select('role_names', 'name',
'roleid = :roleid AND contextid = :ctxid', array('ctxid' => $ctx->id, 'roleid' => $roleids['teacher'])));
$this->assertEquals('Manager', $DB->get_field_select('role_names', 'name',
'roleid = :roleid AND contextid = :ctxid', array('ctxid' => $ctx->id, 'roleid' => $roleids['manager'])));
// Enrolment methods.
$enroldata = array();
$instances = enrol_get_instances($course->id, false);
$this->assertCount(2, $instances);
foreach ($instances as $instance) {
$enroldata[$instance->enrol] = $instance;
}
$this->assertNotEmpty($enroldata['guest']);
$this->assertEquals(ENROL_INSTANCE_DISABLED, $enroldata['guest']->status);
$this->assertNotEmpty($enroldata['self']);
$this->assertEquals($data['enrolment_2_roleid'], $enroldata['self']->roleid);
$this->assertEquals(ENROL_INSTANCE_ENABLED, $enroldata['self']->status);
}
public function test_default_data_saved() {
global $DB;
$this->resetAfterTest(true);
// Create.
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
$updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
$data = array(
'shortname' => 'c1',
);
$defaultdata = array(
'fullname' => 'Fullname',
'category' => '1',
'visible' => '0',
'startdate' => '8 June 1990',
'idnumber' => '123abc',
'summary' => 'Summary',
'format' => 'weeks',
'theme' => 'afterburner',
'lang' => 'en',
'newsitems' => '7',
'showgrades' => '0',
'showreports' => '1',
'legacyfiles' => '1',
'maxbytes' => '1234',
'groupmode' => '2',
'groupmodeforce' => '1',
'enablecompletion' => '1',
);
$this->assertFalse($DB->record_exists('course', array('shortname' => 'c1')));
$co = new tool_uploadcourse_course($mode, $updatemode, $data, $defaultdata);
$this->assertTrue($co->prepare());
$co->proceed();
$this->assertTrue($DB->record_exists('course', array('shortname' => 'c1')));
$course = $DB->get_record('course', array('shortname' => 'c1'));
$ctx = context_course::instance($course->id);
$this->assertEquals($defaultdata['fullname'], $course->fullname);
$this->assertEquals($defaultdata['category'], $course->category);
$this->assertEquals($defaultdata['visible'], $course->visible);
$this->assertEquals(mktime(0, 0, 0, 6, 8, 1990), $course->startdate);
$this->assertEquals($defaultdata['idnumber'], $course->idnumber);
$this->assertEquals($defaultdata['summary'], $course->summary);
$this->assertEquals($defaultdata['format'], $course->format);
$this->assertEquals($defaultdata['theme'], $course->theme);
$this->assertEquals($defaultdata['lang'], $course->lang);
$this->assertEquals($defaultdata['newsitems'], $course->newsitems);
$this->assertEquals($defaultdata['showgrades'], $course->showgrades);
$this->assertEquals($defaultdata['showreports'], $course->showreports);
$this->assertEquals($defaultdata['legacyfiles'], $course->legacyfiles);
$this->assertEquals($defaultdata['maxbytes'], $course->maxbytes);
$this->assertEquals($defaultdata['groupmode'], $course->groupmode);
$this->assertEquals($defaultdata['groupmodeforce'], $course->groupmodeforce);
$this->assertEquals($defaultdata['enablecompletion'], $course->enablecompletion);
// Update.
$cat = $this->getDataGenerator()->create_category();
$mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
$updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_OR_DEFAUTLS;
$data = array(
'shortname' => 'c1',
);
$defaultdata = array(
'fullname' => 'Fullname 2',
'category' => $cat->id,
'visible' => '1',
'startdate' => '11 June 1984',
'idnumber' => 'changedid',
'summary' => 'Summary 2',
'format' => 'topics',
'theme' => 'clean',
'lang' => '',
'newsitems' => '2',
'showgrades' => '1',
'showreports' => '0',
'legacyfiles' => '0',
'maxbytes' => '1111',
'groupmode' => '1',
'groupmodeforce' => '0',
'enablecompletion' => '0',
);
$this->assertTrue($DB->record_exists('course', array('shortname' => 'c1')));
$co = new tool_uploadcourse_course($mode, $updatemode, $data, $defaultdata);
$this->assertTrue($co->prepare());
$co->proceed();
$this->assertTrue($DB->record_exists('course', array('shortname' => 'c1')));
$course = $DB->get_record('course', array('shortname' => 'c1'));
$ctx = context_course::instance($course->id);
$this->assertEquals($defaultdata['fullname'], $course->fullname);
$this->assertEquals($defaultdata['category'], $course->category);
$this->assertEquals($defaultdata['visible'], $course->visible);
$this->assertEquals(mktime(0, 0, 0, 6, 11, 1984), $course->startdate);
$this->assertEquals($defaultdata['idnumber'], $course->idnumber);
$this->assertEquals($defaultdata['summary'], $course->summary);
$this->assertEquals($defaultdata['format'], $course->format);
$this->assertEquals($defaultdata['theme'], $course->theme);
$this->assertEquals($defaultdata['lang'], $course->lang);
$this->assertEquals($defaultdata['newsitems'], $course->newsitems);
$this->assertEquals($defaultdata['showgrades'], $course->showgrades);
$this->assertEquals($defaultdata['showreports'], $course->showreports);
$this->assertEquals($defaultdata['legacyfiles'], $course->legacyfiles);
$this->assertEquals($defaultdata['maxbytes'], $course->maxbytes);
$this->assertEquals($defaultdata['groupmode'], $course->groupmode);
$this->assertEquals($defaultdata['groupmodeforce'], $course->groupmodeforce);
$this->assertEquals($defaultdata['enablecompletion'], $course->enablecompletion);
}
public function test_rename() {
global $DB;
$this->resetAfterTest(true);
$c1 = $this->getDataGenerator()->create_course(array('shortname' => 'c1'));
$c2 = $this->getDataGenerator()->create_course(array('shortname' => 'c2'));
// Cannot rename when creating.
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
$updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
$importoptions = array('canrename' => true);
$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', '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;
$updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
$importoptions = array('canrename' => false);
$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;
$updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
$importoptions = array('canrename' => true);
$data = array('shortname' => 'c1', 'rename' => 'newshortname');
$co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
$this->assertTrue($co->prepare());
$co->proceed();
$this->assertEquals('newshortname', $DB->get_field_select('course', 'shortname', 'id = :id', array('id' => $c1->id)));
// Can rename when updating.
$mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
$updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
$importoptions = array('canrename' => true);
$data = array('shortname' => 'newshortname', 'rename' => 'newshortname2');
$co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
$this->assertTrue($co->prepare());
$co->proceed();
$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_CREATE_OR_UPDATE;
$updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
$importoptions = array('canrename' => true);
$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;
$updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_OR_DEFAUTLS;
$importoptions = array('canrename' => true);
$data = array('shortname' => 'newshortname2', 'rename' => 'c1', 'fullname' => 'Another fullname!');
$defaultdata = array('summary' => 'New summary!');
$co = new tool_uploadcourse_course($mode, $updatemode, $data, $defaultdata, $importoptions);
$this->assertTrue($co->prepare());
$co->proceed();
$this->assertEquals('c1', $DB->get_field_select('course', 'shortname', 'id = :id', array('id' => $c1->id)));
$this->assertEquals('New summary!', $DB->get_field_select('course', 'summary', 'id = :id', array('id' => $c1->id)));
$this->assertEquals('Another fullname!', $DB->get_field_select('course', 'fullname', 'id = :id', array('id' => $c1->id)));
// Renaming with invalid shortname.
$mode = tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE;
$updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
$importoptions = array('canrename' => true);
$data = array('shortname' => 'c1', 'rename' => '<span>invalid</span>');
$co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
$this->assertFalse($co->prepare());
$this->assertArrayHasKey('invalidshortname', $co->get_errors());
// Renaming with invalid shortname.
$mode = tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE;
$updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
$importoptions = array('canrename' => true);
$data = array('shortname' => 'c1', 'rename' => 'c2');
$co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
$this->assertFalse($co->prepare());
$this->assertArrayHasKey('cannotrenameshortnamealreadyinuse', $co->get_errors());
}
public function test_restore_course() {
global $DB;
$this->resetAfterTest(true);
$this->setAdminUser();
$c1 = $this->getDataGenerator()->create_course();
$c1f1 = $this->getDataGenerator()->create_module('forum', array('course' => $c1->id));
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
$updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
$data = array('shortname' => 'A1', 'templatecourse' => $c1->shortname, 'summary' => 'A', 'category' => 1,
'fullname' => 'A1');
$co = new tool_uploadcourse_course($mode, $updatemode, $data);
$this->assertTrue($co->prepare());
$co->proceed();
$course = $DB->get_record('course', array('shortname' => 'A1'));
$modinfo = get_fast_modinfo($course);
$found = false;
foreach ($modinfo->get_cms() as $cmid => $cm) {
if ($cm->modname == 'forum' && $cm->name == $c1f1->name) {
$found = true;
break;
}
}
$this->assertTrue($found);
// Restore the time limit to prevent warning.
set_time_limit(0);
}
public function test_restore_file() {
global $DB;
$this->resetAfterTest(true);
$this->setAdminUser();
$c1 = $this->getDataGenerator()->create_course();
$c1f1 = $this->getDataGenerator()->create_module('forum', array('course' => $c1->id));
// Restore from a file, checking that the file takes priority over the templatecourse.
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
$updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
$data = array('shortname' => 'A1', 'backupfile' => __DIR__ . '/fixtures/backup.mbz',
'summary' => 'A', 'category' => 1, 'fullname' => 'A1', 'templatecourse' => $c1->shortname);
$co = new tool_uploadcourse_course($mode, $updatemode, $data);
$this->assertTrue($co->prepare());
$co->proceed();
$course = $DB->get_record('course', array('shortname' => 'A1'));
$modinfo = get_fast_modinfo($course);
$found = false;
foreach ($modinfo->get_cms() as $cmid => $cm) {
if ($cm->modname == 'glossary' && $cm->name == 'Imported Glossary') {
$found = true;
} else if ($cm->modname == 'forum' && $cm->name == $c1f1->name) {
// We should not find this!
$this->assertTrue(false);
}
}
$this->assertTrue($found);
// Restore the time limit to prevent warning.
set_time_limit(0);
}
/**
* Testing the reset on groups, group members and enrolments.
*/
public function test_reset() {
global $DB;
$this->resetAfterTest(true);
$c1 = $this->getDataGenerator()->create_course();
$c1ctx = context_course::instance($c1->id);
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
$teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
$u1 = $this->getDataGenerator()->create_user();
$this->getDataGenerator()->enrol_user($u1->id, $c1->id, $studentrole->id);
$this->assertCount(1, get_enrolled_users($c1ctx));
$g1 = $this->getDataGenerator()->create_group(array('courseid' => $c1->id));
$this->getDataGenerator()->create_group_member(array('groupid' => $g1->id, 'userid' => $u1->id));
$this->assertEquals(1, $DB->count_records('groups', array('courseid' => $c1->id)));
$this->assertTrue($DB->record_exists('groups_members', array('groupid' => $g1->id, 'userid' => $u1->id)));
// Wrong mode.
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
$updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
$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));
// Reset not allowed.
$mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
$updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
$data = array('shortname' => $c1->shortname, 'reset' => '1');
$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));
// Reset allowed but not requested.
$mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
$updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
$data = array('shortname' => $c1->shortname, 'reset' => '0');
$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));
// Reset passed as a default parameter, should not be taken in account.
$mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
$updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
$data = array('shortname' => $c1->shortname);
$importoptions = array('canreset' => true);
$co = new tool_uploadcourse_course($mode, $updatemode, $data, array('reset' => 1), $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));
// Reset executed from data.
$mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
$updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
$data = array('shortname' => $c1->shortname, 'reset' => 1);
$importoptions = array('canreset' => true);
$co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
$this->assertTrue($co->prepare());
$co->proceed();
$this->assertFalse($DB->record_exists('groups', array('id' => $g1->id)));
$this->assertFalse($DB->record_exists('groups_members', array('groupid' => $g1->id, 'userid' => $u1->id)));
$this->assertCount(0, get_enrolled_users($c1ctx));
// Reset executed from import option.
$mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
$updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
$data = array('shortname' => $c1->shortname, 'reset' => 0);
$importoptions = array('reset' => 1, 'canreset' => true);
$co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
$g1 = $this->getDataGenerator()->create_group(array('courseid' => $c1->id));
$this->getDataGenerator()->create_group_member(array('groupid' => $g1->id, 'userid' => $u1->id));
$this->assertEquals(1, $DB->count_records('groups', array('courseid' => $c1->id)));
$this->assertTrue($co->prepare());
$co->proceed();
$this->assertFalse($DB->record_exists('groups', array('id' => $g1->id)));
}
public function test_create_bad_category() {
$this->resetAfterTest(true);
// Ensure fails when category cannot be resolved upon creation.
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
$updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
$data = array('shortname' => 'c1', 'summary' => 'summary', 'fullname' => 'FN', 'category' => 'Wrong cat');
$co = new tool_uploadcourse_course($mode, $updatemode, $data);
$this->assertFalse($co->prepare());
$this->assertArrayHasKey('couldnotresolvecatgorybyid', $co->get_errors());
// Ensure fails when category cannot be resolved upon update.
$c1 = $this->getDataGenerator()->create_course();
$mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
$updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
$data = array('shortname' => $c1->shortname, 'category' => 'Wrong cat');
$co = new tool_uploadcourse_course($mode, $updatemode, $data);
$this->assertFalse($co->prepare());
$this->assertArrayHasKey('couldnotresolvecatgorybyid', $co->get_errors());
}
public function test_enrolment_data() {
$this->resetAfterTest(true);
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
$updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
$data = array('shortname' => 'c1', 'summary' => 'S', 'fullname' => 'FN', 'category' => '1');
$data['enrolment_1'] = 'manual';
$data['enrolment_1_role'] = 'teacher';
$data['enrolment_1_startdate'] = '2nd July 2013';
$data['enrolment_1_enddate'] = '2nd August 2013';
$data['enrolment_1_enrolperiod'] = '10 days';
$co = new tool_uploadcourse_course($mode, $updatemode, $data);
$this->assertTrue($co->prepare());
$co->proceed();
// Enrolment methods.
$enroldata = array();
$instances = enrol_get_instances($co->get_id(), false);
foreach ($instances as $instance) {
$enroldata[$instance->enrol] = $instance;
}
$this->assertNotEmpty($enroldata['manual']);
$this->assertEquals(ENROL_INSTANCE_ENABLED, $enroldata['manual']->status);
$this->assertEquals(strtotime($data['enrolment_1_startdate']), $enroldata['manual']->enrolstartdate);
$this->assertEquals(strtotime('1970-01-01 GMT + ' . $data['enrolment_1_enrolperiod']), $enroldata['manual']->enrolperiod);
$this->assertEquals(strtotime('12th July 2013'), $enroldata['manual']->enrolenddate);
}
public function test_idnumber_problems() {
$this->resetAfterTest(true);
$c1 = $this->getDataGenerator()->create_course(array('shortname' => 'sntaken', 'idnumber' => 'taken'));
$c2 = $this->getDataGenerator()->create_course();
// Create with existing ID number.
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
$updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
$data = array('shortname' => 'c2', 'summary' => 'summary', 'fullname' => 'FN', 'category' => '1',
'idnumber' => $c1->idnumber);
$co = new tool_uploadcourse_course($mode, $updatemode, $data);
$this->assertFalse($co->prepare());
$this->assertArrayHasKey('idnumberalreadyinuse', $co->get_errors());
// Rename to existing ID number.
$mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
$updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
$data = array('shortname' => $c2->shortname, 'rename' => 'SN', 'idnumber' => $c1->idnumber);
$importoptions = array('canrename' => true);
$co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
$this->assertFalse($co->prepare());
$this->assertArrayHasKey('cannotrenameidnumberconflict', $co->get_errors());
// Incrementing shortname increments idnumber.
$mode = tool_uploadcourse_processor::MODE_CREATE_ALL;
$updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
$data = array('shortname' => $c1->shortname, 'idnumber' => $c1->idnumber, 'summary' => 'S', 'fullname' => 'F',
'category' => 1);
$co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), array());
$this->assertTrue($co->prepare());
$this->assertArrayHasKey('courseshortnameincremented', $co->get_statuses());
$this->assertArrayHasKey('courseidnumberincremented', $co->get_statuses());
$data = $co->get_data();
$this->assertEquals('sntaken_2', $data['shortname']);
$this->assertEquals('taken_2', $data['idnumber']);
// Incrementing shortname increments idnumber unless available.
$mode = tool_uploadcourse_processor::MODE_CREATE_ALL;
$updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
$data = array('shortname' => $c1->shortname, 'idnumber' => 'nottaken', 'summary' => 'S', 'fullname' => 'F',
'category' => 1);
$co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), array());
$this->assertTrue($co->prepare());
$this->assertArrayHasKey('courseshortnameincremented', $co->get_statuses());
$this->assertArrayNotHasKey('courseidnumberincremented', $co->get_statuses());
$data = $co->get_data();
$this->assertEquals('sntaken_2', $data['shortname']);
$this->assertEquals('nottaken', $data['idnumber']);
}
public function test_generate_shortname() {
$this->resetAfterTest(true);
$c1 = $this->getDataGenerator()->create_course(array('shortname' => 'taken'));
// Generate a shortname.
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
$updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
$data = array('summary' => 'summary', 'fullname' => 'FN', 'category' => '1', 'idnumber' => 'IDN');
$importoptions = array('shortnametemplate' => '%i');
$co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
$this->assertTrue($co->prepare());
$this->assertArrayHasKey('courseshortnamegenerated', $co->get_statuses());
// Generate a shortname without a template.
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
$updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
$data = array('summary' => 'summary', 'fullname' => 'FN', 'category' => '1');
$co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), array());
$this->assertFalse($co->prepare());
$this->assertArrayHasKey('missingshortnamenotemplate', $co->get_errors());
// Generate a shortname in update mode.
$mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
$updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
$data = array('summary' => 'summary', 'fullname' => 'FN', 'category' => '1');
$importoptions = array('shortnametemplate' => '%f');
$co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
$this->assertFalse($co->prepare());
// Commented because we never get here as the course without shortname does not exist.
// $this->assertArrayHasKey('cannotgenerateshortnameupdatemode', $co->get_errors());
// Generate a shortname to a course that already exists.
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
$updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
$data = array('summary' => 'summary', 'fullname' => 'taken', 'category' => '1');
$importoptions = array('shortnametemplate' => '%f');
$co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
$this->assertFalse($co->prepare());
$this->assertArrayHasKey('generatedshortnamealreadyinuse', $co->get_errors());
// Generate a shortname to a course that already exists will be incremented.
$mode = tool_uploadcourse_processor::MODE_CREATE_ALL;
$updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
$data = array('summary' => 'summary', 'fullname' => 'taken', 'category' => '1');
$importoptions = array('shortnametemplate' => '%f');
$co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
$this->assertTrue($co->prepare());
$this->assertArrayHasKey('courseshortnamegenerated', $co->get_statuses());
$this->assertArrayHasKey('courseshortnameincremented', $co->get_statuses());
}
}
Binary file not shown.
+4
View File
@@ -0,0 +1,4 @@
shortname,fullname,summary,category,idnumber
C1,Course 1,Summary 1,1,ID1
C2,Course 2,Summary 2,1,ID2
C3,Course 3,Summary 3,1,ID3
1 shortname fullname summary category idnumber
2 C1 Course 1 Summary 1 1 ID1
3 C2 Course 2 Summary 2 1 ID2
4 C3 Course 3 Summary 3 1 ID3
@@ -0,0 +1,389 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* File containing tests for the helper.
*
* @package tool_uploadcourse
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
/**
* Helper test case.
*
* @package tool_uploadcourse
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class tool_uploadcourse_helper_testcase extends advanced_testcase {
public function test_generate_shortname() {
$data = (object) array('fullname' => 'Ah bh Ch 01 02 03', 'idnumber' => 'ID123');
$this->assertSame($data->fullname, tool_uploadcourse_helper::generate_shortname($data, '%f'));
$this->assertSame($data->idnumber, tool_uploadcourse_helper::generate_shortname($data, '%i'));
$this->assertSame('Ah Bh Ch', tool_uploadcourse_helper::generate_shortname($data, '%~8f'));
$this->assertSame('AH BH CH', tool_uploadcourse_helper::generate_shortname($data, '%+8f'));
$this->assertSame('id123', tool_uploadcourse_helper::generate_shortname($data, '%-i'));
$this->assertSame('[Ah bh Ch] = ID123', tool_uploadcourse_helper::generate_shortname($data, '[%8f] = %i'));
$this->assertSame('0', tool_uploadcourse_helper::generate_shortname($data, '0'));
$this->assertSame('%unknown', tool_uploadcourse_helper::generate_shortname($data, '%unknown'));
$this->assertNull(tool_uploadcourse_helper::generate_shortname($data, ''));
$this->assertNull(tool_uploadcourse_helper::generate_shortname(array(), '%f'));
}
public function test_get_course_formats() {
$result = tool_uploadcourse_helper::get_course_formats();
$this->assertSame(array_keys(get_plugin_list('format')), $result);
// Should be similar as first result, as cached.
$this->assertSame($result, tool_uploadcourse_helper::get_course_formats());
}
public function test_get_enrolment_data() {
$this->resetAfterTest(true);
$data = array(
'enrolment_1' => 'unknown',
'enrolment_1_foo' => '1',
'enrolment_1_bar' => '2',
'enrolment_2' => 'self',
'enrolment_2_delete' => '1',
'enrolment_2_foo' => 'a',
'enrolment_2_bar' => '1',
'enrolment_3' => 'manual',
'enrolment_3_disable' => '2',
'enrolment_3_foo' => 'b',
'enrolment_3_bar' => '2',
'enrolment_4' => 'database',
'enrolment_4_foo' => 'x',
'enrolment_4_bar' => '3',
'enrolment_5_test3' => 'test3',
'enrolment_5_test2' => 'test2',
'enrolment_5_test1' => 'test1',
'enrolment_5' => 'flatfile',
);
$expected = array(
'self' => array(
'delete' => '1',
'foo' => 'a',
'bar' => '1',
),
'manual' => array(
'disable' => '2',
'foo' => 'b',
'bar' => '2',
),
'database' => array(
'foo' => 'x',
'bar' => '3',
),
'flatfile' => array(
'test3' => 'test3',
'test2' => 'test2',
'test1' => 'test1',
)
);
$this->assertSame(tool_uploadcourse_helper::get_enrolment_data($data), $expected);
}
public function test_get_enrolment_plugins() {
$this->resetAfterTest(true);
$actual = tool_uploadcourse_helper::get_enrolment_plugins();
$this->assertSame(array_keys(enrol_get_plugins(false)), array_keys($actual));
// This should be identical as cached.
$secondactual = tool_uploadcourse_helper::get_enrolment_plugins();
$this->assertEquals($actual, $secondactual);
}
public function test_get_restore_content_dir() {
global $CFG;
$this->resetAfterTest(true);
$this->setAdminUser();
$c1 = $this->getDataGenerator()->create_course();
$c2 = $this->getDataGenerator()->create_course((object) array('shortname' => 'Yay'));
// Creating backup file.
$bc = new backup_controller(backup::TYPE_1COURSE, $c1->id, backup::FORMAT_MOODLE,
backup::INTERACTIVE_NO, backup::MODE_GENERAL, 2);
$bc->execute_plan();
$result = $bc->get_results();
$this->assertTrue(isset($result['backup_destination']));
$c1backupfile = $result['backup_destination']->copy_content_to_temp();
$bc->destroy();
// Creating backup file.
$bc = new backup_controller(backup::TYPE_1COURSE, $c2->id, backup::FORMAT_MOODLE,
backup::INTERACTIVE_NO, backup::MODE_GENERAL, 2);
$bc->execute_plan();
$result = $bc->get_results();
$this->assertTrue(isset($result['backup_destination']));
$c2backupfile = $result['backup_destination']->copy_content_to_temp();
$bc->destroy();
// Checking restore dir.
$dir = tool_uploadcourse_helper::get_restore_content_dir($c1backupfile, null);
$bcinfo = backup_general_helper::get_backup_information($dir);
$this->assertEquals($bcinfo->original_course_id, $c1->id);
$this->assertEquals($bcinfo->original_course_fullname, $c1->fullname);
// Do it again, it should be the same directory.
$dir2 = tool_uploadcourse_helper::get_restore_content_dir($c1backupfile, null);
$this->assertEquals($dir, $dir2);
// Get the second course.
$dir = tool_uploadcourse_helper::get_restore_content_dir($c2backupfile, null);
$bcinfo = backup_general_helper::get_backup_information($dir);
$this->assertEquals($bcinfo->original_course_id, $c2->id);
$this->assertEquals($bcinfo->original_course_fullname, $c2->fullname);
// Checking with a shortname.
$dir = tool_uploadcourse_helper::get_restore_content_dir(null, $c1->shortname);
$bcinfo = backup_general_helper::get_backup_information($dir);
$this->assertEquals($bcinfo->original_course_id, $c1->id);
$this->assertEquals($bcinfo->original_course_fullname, $c1->fullname);
// Do it again, it should be the same directory.
$dir2 = tool_uploadcourse_helper::get_restore_content_dir(null, $c1->shortname);
$this->assertEquals($dir, $dir2);
// Get the second course.
$dir = tool_uploadcourse_helper::get_restore_content_dir(null, $c2->shortname);
$bcinfo = backup_general_helper::get_backup_information($dir);
$this->assertEquals($bcinfo->original_course_id, $c2->id);
$this->assertEquals($bcinfo->original_course_fullname, $c2->fullname);
// Get a course that does not exist.
$errors = array();
$dir = tool_uploadcourse_helper::get_restore_content_dir(null, 'DoesNotExist', $errors);
$this->assertFalse($dir);
$this->assertArrayHasKey('coursetorestorefromdoesnotexist', $errors);
// Cleaning content directories.
$oldcfg = isset($CFG->keeptempdirectoriesonbackup) ? $CFG->keeptempdirectoriesonbackup : false;
$dir = "$CFG->tempdir/backup/$dir";
$this->assertTrue(file_exists($dir));
$CFG->keeptempdirectoriesonbackup = false;
tool_uploadcourse_helper::clean_restore_content();
$this->assertTrue(file_exists($dir));
$CFG->keeptempdirectoriesonbackup = true;
tool_uploadcourse_helper::clean_restore_content();
$this->assertFalse(file_exists($dir));
$CFG->keeptempdirectoriesonbackup = $oldcfg;
// Restore the time limit to prevent warning.
set_time_limit(0);
}
public function test_get_role_ids() {
$this->getDataGenerator();
// Mimic function result.
$expected = array();
$roles = get_all_roles();
foreach ($roles as $role) {
$expected[$role->shortname] = $role->id;
}
$actual = tool_uploadcourse_helper::get_role_ids();
$this->assertSame($actual, $expected);
// Check cache.
$this->assertSame($actual, tool_uploadcourse_helper::get_role_ids());
}
public function test_get_role_names() {
$this->resetAfterTest(true);
create_role('Villain', 'villain', 'The bad guys');
$data = array(
'role_student' => 'Padawan',
'role_teacher' => 'Guardian',
'role_editingteacher' => 'Knight',
'role_manager' => 'Master',
'role_villain' => 'Jabba the Hutt',
'role_android' => 'R2D2',
);
// Get the role IDs, but need to force the cache reset as a new role is defined.
$roleids = tool_uploadcourse_helper::get_role_ids(true);
$expected = array(
'role_' . $roleids['student'] => 'Padawan',
'role_' . $roleids['teacher'] => 'Guardian',
'role_' . $roleids['editingteacher'] => 'Knight',
'role_' . $roleids['manager'] => 'Master',
'role_' . $roleids['villain'] => 'Jabba the Hutt',
);
$errors = array();
$actual = tool_uploadcourse_helper::get_role_names($data, $errors);
$this->assertSame($actual, $expected);
$this->assertArrayHasKey('invalidroles', $errors);
}
public function test_increment_idnumber() {
$this->resetAfterTest(true);
$c1 = $this->getDataGenerator()->create_course(array('idnumber' => 'C1'));
$c2 = $this->getDataGenerator()->create_course(array('idnumber' => 'C2'));
$c3 = $this->getDataGenerator()->create_course(array('idnumber' => 'Yo'));
$this->assertEquals('C3', tool_uploadcourse_helper::increment_idnumber('C1'));
$this->assertEquals('Yo_2', tool_uploadcourse_helper::increment_idnumber('Yo'));
$this->assertEquals('DoesNotExist', tool_uploadcourse_helper::increment_idnumber('DoesNotExist'));
}
public function test_increment_shortname() {
$this->resetAfterTest(true);
$c1 = $this->getDataGenerator()->create_course(array('shortname' => 'C1'));
$c2 = $this->getDataGenerator()->create_course(array('shortname' => 'C2'));
$c3 = $this->getDataGenerator()->create_course(array('shortname' => 'Yo'));
// FYI: increment_shortname assumes that the course exists, and so increment the shortname immediately.
$this->assertEquals('C3', tool_uploadcourse_helper::increment_shortname('C1'));
$this->assertEquals('Yo_2', tool_uploadcourse_helper::increment_shortname('Yo'));
$this->assertEquals('DoesNotExist_2', tool_uploadcourse_helper::increment_shortname('DoesNotExist'));
}
public function test_resolve_category() {
$this->resetAfterTest(true);
$c1 = $this->getDataGenerator()->create_category(array('name' => 'First level'));
$c2 = $this->getDataGenerator()->create_category(array('name' => 'Second level', 'parent' => $c1->id));
$c3 = $this->getDataGenerator()->create_category(array('idnumber' => 'C3'));
$data = array(
'category' => $c1->id,
'category_path' => $c1->name . ' / ' . $c2->name,
'category_idnumber' => $c3->idnumber,
);
$this->assertEquals($c1->id, tool_uploadcourse_helper::resolve_category($data));
unset($data['category']);
$this->assertEquals($c3->id, tool_uploadcourse_helper::resolve_category($data));
unset($data['category_idnumber']);
$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, $errors));
$this->assertArrayHasKey('couldnotresolvecatgorybyidnumber', $errors);
$errors = array();
$data['category'] = 1234;
$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, $errors));
$this->assertArrayHasKey('couldnotresolvecatgorybypath', $errors);
}
public function test_resolve_category_by_idnumber() {
$this->resetAfterTest(true);
$c1 = $this->getDataGenerator()->create_category(array('idnumber' => 'C1'));
$c2 = $this->getDataGenerator()->create_category(array('idnumber' => 'C2'));
// Doubled for cache check.
$this->assertEquals($c1->id, tool_uploadcourse_helper::resolve_category_by_idnumber('C1'));
$this->assertEquals($c1->id, tool_uploadcourse_helper::resolve_category_by_idnumber('C1'));
$this->assertEquals($c2->id, tool_uploadcourse_helper::resolve_category_by_idnumber('C2'));
$this->assertEquals($c2->id, tool_uploadcourse_helper::resolve_category_by_idnumber('C2'));
$this->assertEmpty(tool_uploadcourse_helper::resolve_category_by_idnumber('DoesNotExist'));
$this->assertEmpty(tool_uploadcourse_helper::resolve_category_by_idnumber('DoesNotExist'));
}
public function test_resolve_category_by_path() {
$this->resetAfterTest(true);
$cat1 = $this->getDataGenerator()->create_category(array('name' => 'Cat 1'));
$cat1_1 = $this->getDataGenerator()->create_category(array('name' => 'Cat 1.1', 'parent' => $cat1->id));
$cat1_1_1 = $this->getDataGenerator()->create_category(array('name' => 'Cat 1.1.1', 'parent' => $cat1_1->id));
$cat1_1_2 = $this->getDataGenerator()->create_category(array('name' => 'Cat 1.1.2', 'parent' => $cat1_1->id));
$cat1_2 = $this->getDataGenerator()->create_category(array('name' => 'Cat 1.2', 'parent' => $cat1->id));
$cat2 = $this->getDataGenerator()->create_category(array('name' => 'Cat 2'));
$cat2_1 = $this->getDataGenerator()->create_category(array('name' => 'Cat 2.1', 'parent' => $cat2->id, 'visible' => false));
$cat2_1_1 = $this->getDataGenerator()->create_category(array('name' => 'Cat 2.1.1', 'parent' => $cat2_1->id));
$cat2_1_2 = $this->getDataGenerator()->create_category(array('name' => 'Cat 2.1.2', 'parent' => $cat2_1->id));
$cat2_2 = $this->getDataGenerator()->create_category(array('name' => 'Cat 2.2', 'parent' => $cat2->id));
$cat3 = $this->getDataGenerator()->create_category(array('name' => 'Cat 3'));
$cat3_1 = $this->getDataGenerator()->create_category(array('name' => 'Cat 3.1 Doubled', 'parent' => $cat3->id));
$cat3_1b = $this->getDataGenerator()->create_category(array('name' => 'Cat 3.1 Doubled', 'parent' => $cat3->id));
$cat3_1_1 = $this->getDataGenerator()->create_category(array('name' => 'Cat 3.1.1', 'parent' => $cat3_1->id));
$cat3_fakedouble = $this->getDataGenerator()->create_category(array('name' => 'Cat 3.1.1', 'parent' => $cat3->id));
// Existing categories. Doubled for cache testing.
$path = array('Cat 1');
$this->assertEquals($cat1->id, tool_uploadcourse_helper::resolve_category_by_path($path));
$this->assertEquals($cat1->id, tool_uploadcourse_helper::resolve_category_by_path($path));
$path = array('Cat 1', 'Cat 1.1', 'Cat 1.1.2');
$this->assertEquals($cat1_1_2->id, tool_uploadcourse_helper::resolve_category_by_path($path));
$this->assertEquals($cat1_1_2->id, tool_uploadcourse_helper::resolve_category_by_path($path));
$path = array('Cat 1', 'Cat 1.2');
$this->assertEquals($cat1_2->id, tool_uploadcourse_helper::resolve_category_by_path($path));
$this->assertEquals($cat1_2->id, tool_uploadcourse_helper::resolve_category_by_path($path));
$path = array('Cat 2');
$this->assertEquals($cat2->id, tool_uploadcourse_helper::resolve_category_by_path($path));
$this->assertEquals($cat2->id, tool_uploadcourse_helper::resolve_category_by_path($path));
// Hidden category.
$path = array('Cat 2', 'Cat 2.1');
$this->assertEquals($cat2_1->id, tool_uploadcourse_helper::resolve_category_by_path($path));
$this->assertEquals($cat2_1->id, tool_uploadcourse_helper::resolve_category_by_path($path));
// Hidden parent.
$path = array('Cat 2', 'Cat 2.1', 'Cat 2.1.2');
$this->assertEquals($cat2_1_2->id, tool_uploadcourse_helper::resolve_category_by_path($path));
$this->assertEquals($cat2_1_2->id, tool_uploadcourse_helper::resolve_category_by_path($path));
// Does not exist.
$path = array('No cat 3', 'Cat 1.2');
$this->assertEmpty(tool_uploadcourse_helper::resolve_category_by_path($path));
$this->assertEmpty(tool_uploadcourse_helper::resolve_category_by_path($path));
$path = array('Cat 2', 'Cat 2.x');
$this->assertEmpty(tool_uploadcourse_helper::resolve_category_by_path($path));
$this->assertEmpty(tool_uploadcourse_helper::resolve_category_by_path($path));
// Name conflict.
$path = array('Cat 3', 'Cat 3.1 Doubled');
$this->assertEmpty(tool_uploadcourse_helper::resolve_category_by_path($path));
$this->assertEmpty(tool_uploadcourse_helper::resolve_category_by_path($path));
$path = array('Cat 3', 'Cat 3.1 Doubled', 'Cat 3.1.1');
$this->assertEmpty(tool_uploadcourse_helper::resolve_category_by_path($path));
$this->assertEmpty(tool_uploadcourse_helper::resolve_category_by_path($path));
$path = array('Cat 3', 'Cat 3.1.1');
$this->assertEquals($cat3_fakedouble->id, tool_uploadcourse_helper::resolve_category_by_path($path));
$this->assertEquals($cat3_fakedouble->id, tool_uploadcourse_helper::resolve_category_by_path($path));
}
}
@@ -0,0 +1,225 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* File containing tests for the processor.
*
* @package tool_uploadcourse
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . '/csvlib.class.php');
/**
* Processor test case.
*
* @package tool_uploadcourse
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class tool_uploadcourse_processor_testcase extends advanced_testcase {
public function test_basic() {
global $DB;
$this->resetAfterTest(true);
$content = array(
"shortname,fullname,summary",
"c1,Course 1,Course 1 summary",
"c2,Course 2,Course 2 summary",
);
$content = implode("\n", $content);
$iid = csv_import_reader::get_new_iid('uploadcourse');
$cir = new csv_import_reader($iid, 'uploadcourse');
$cir->load_csv_content($content, 'utf-8', 'comma');
$cir->init();
$options = array('mode' => tool_uploadcourse_processor::MODE_CREATE_ALL);
$defaults = array('category' => '1');
$p = new tool_uploadcourse_processor($cir, $options, $defaults);
$this->assertFalse($DB->record_exists('course', array('shortname' => 'c1')));
$this->assertFalse($DB->record_exists('course', array('shortname' => 'c2')));
$p->execute();
$this->assertTrue($DB->record_exists('course', array('shortname' => 'c1')));
$this->assertTrue($DB->record_exists('course', array('shortname' => 'c2')));
}
public function test_restore_template_course() {
global $DB;
$this->resetAfterTest(true);
$this->setAdminUser();
$c1 = $this->getDataGenerator()->create_course();
$c1f1 = $this->getDataGenerator()->create_module('forum', array('course' => $c1->id));
$content = array(
"shortname,fullname,summary",
"c2,Course 2,Course 2 summary",
);
$content = implode("\n", $content);
$iid = csv_import_reader::get_new_iid('uploadcourse');
$cir = new csv_import_reader($iid, 'uploadcourse');
$cir->load_csv_content($content, 'utf-8', 'comma');
$cir->init();
$options = array('mode' => tool_uploadcourse_processor::MODE_CREATE_NEW, 'templatecourse' => $c1->shortname);
$defaults = array('category' => '1');
$p = new tool_uploadcourse_processor($cir, $options, $defaults);
$this->assertFalse($DB->record_exists('course', array('shortname' => 'c2')));
$p->execute();
$c2 = $DB->get_record('course', array('shortname' => 'c2'));
$modinfo = get_fast_modinfo($c2);
$found = false;
foreach ($modinfo->get_cms() as $cmid => $cm) {
if ($cm->modname == 'forum' && $cm->name == $c1f1->name) {
$found = true;
break;
}
}
$this->assertTrue($found);
// Restore the time limit to prevent warning.
set_time_limit(0);
}
public function test_restore_restore_file() {
global $DB;
$this->resetAfterTest(true);
$this->setAdminUser();
$content = array(
"shortname,fullname,summary",
"c1,Course 1,Course 1 summary",
);
$content = implode("\n", $content);
$iid = csv_import_reader::get_new_iid('uploadcourse');
$cir = new csv_import_reader($iid, 'uploadcourse');
$cir->load_csv_content($content, 'utf-8', 'comma');
$cir->init();
$options = array(
'mode' => tool_uploadcourse_processor::MODE_CREATE_NEW,
'restorefile' => __DIR__ . '/fixtures/backup.mbz',
'templatecourse' => 'DoesNotExist' // Restorefile takes priority.
);
$defaults = array('category' => '1');
$p = new tool_uploadcourse_processor($cir, $options, $defaults);
$this->assertFalse($DB->record_exists('course', array('shortname' => 'c1')));
$p->execute();
$c1 = $DB->get_record('course', array('shortname' => 'c1'));
$modinfo = get_fast_modinfo($c1);
$found = false;
foreach ($modinfo->get_cms() as $cmid => $cm) {
if ($cm->modname == 'glossary' && $cm->name == 'Imported Glossary') {
$found = true;
break;
}
}
$this->assertTrue($found);
// Restore the time limit to prevent warning.
set_time_limit(0);
}
public function test_shortname_template() {
global $DB;
$this->resetAfterTest(true);
$content = array(
"shortname,fullname,summary,idnumber",
",Course 1,C1 Summary,ID123",
);
$content = implode("\n", $content);
$iid = csv_import_reader::get_new_iid('uploadcourse');
$cir = new csv_import_reader($iid, 'uploadcourse');
$cir->load_csv_content($content, 'utf-8', 'comma');
$cir->init();
$options = array('mode' => tool_uploadcourse_processor::MODE_CREATE_NEW, 'shortnametemplate' => '%i: %f');
$defaults = array('category' => '1');
$p = new tool_uploadcourse_processor($cir, $options, $defaults);
$this->assertFalse($DB->record_exists('course', array('idnumber' => 'ID123')));
$p->execute();
$this->assertTrue($DB->record_exists('course', array('idnumber' => 'ID123')));
$c = $DB->get_record('course', array('idnumber' => 'ID123'));
$this->assertEquals('ID123: Course 1', $c->shortname);
}
public function test_empty_csv() {
$this->resetAfterTest(true);
$content = array();
$content = implode("\n", $content);
$iid = csv_import_reader::get_new_iid('uploadcourse');
$cir = new csv_import_reader($iid, 'uploadcourse');
$cir->load_csv_content($content, 'utf-8', 'comma');
$cir->init();
$options = array('mode' => tool_uploadcourse_processor::MODE_CREATE_NEW);
$this->setExpectedException('moodle_exception');
$p = new tool_uploadcourse_processor($cir, $options, array());
}
public function test_not_enough_columns() {
$this->resetAfterTest(true);
$content = array(
"shortname",
"c1",
);
$content = implode("\n", $content);
$iid = csv_import_reader::get_new_iid('uploadcourse');
$cir = new csv_import_reader($iid, 'uploadcourse');
$cir->load_csv_content($content, 'utf-8', 'comma');
$cir->init();
$options = array('mode' => tool_uploadcourse_processor::MODE_CREATE_NEW);
$this->setExpectedException('moodle_exception');
$p = new tool_uploadcourse_processor($cir, $options, array());
}
public function test_preview() {
global $DB;
$this->resetAfterTest(true);
$content = array(
"shortname,fullname,summary",
"c1,Course 1,Course 1 summary",
"c2,Course 2,Course 2 summary",
);
$content = implode("\n", $content);
$iid = csv_import_reader::get_new_iid('uploadcourse');
$cir = new csv_import_reader($iid, 'uploadcourse');
$cir->load_csv_content($content, 'utf-8', 'comma');
$cir->init();
$options = array('mode' => tool_uploadcourse_processor::MODE_CREATE_ALL);
$defaults = array('category' => '1');
$p = new tool_uploadcourse_processor($cir, $options, $defaults);
// Nothing special to expect here, just make sure no exceptions are thrown.
$p->preview();
}
}
+29
View File
@@ -0,0 +1,29 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Plugin version info.
*
* @package tool_uploadcourse
* @copyright 2011 Piers Harding
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2013070200; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2013062100; // Requires this Moodle version.
$plugin->component = 'tool_uploadcourse'; // Full name of the plugin (used for diagnostics).
+1 -1
View File
@@ -840,7 +840,7 @@ class plugin_manager {
'dbtransfer', 'generator', 'health', 'innodb', 'installaddon',
'langimport', 'multilangupgrade', 'phpunit', 'profiling',
'qeupgradehelper', 'replace', 'spamcleaner', 'timezoneimport',
'unittest', 'uploaduser', 'unsuproles', 'xmldb'
'unittest', 'uploadcourse', 'uploaduser', 'unsuproles', 'xmldb'
),
'webservice' => array(