first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
@@ -0,0 +1,98 @@
<?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);
$mform->addHelpButton('options[mode]', 'mode', 'tool_uploadcourse');
$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->hideIf('options[updatemode]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_NEW);
$mform->hideIf('options[updatemode]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_ALL);
$mform->addHelpButton('options[updatemode]', 'updatemode', 'tool_uploadcourse');
$mform->addElement('selectyesno', 'options[allowdeletes]', get_string('allowdeletes', 'tool_uploadcourse'));
$mform->setDefault('options[allowdeletes]', 0);
$mform->hideIf('options[allowdeletes]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_NEW);
$mform->hideIf('options[allowdeletes]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_ALL);
$mform->addHelpButton('options[allowdeletes]', 'allowdeletes', 'tool_uploadcourse');
$mform->addElement('selectyesno', 'options[allowrenames]', get_string('allowrenames', 'tool_uploadcourse'));
$mform->setDefault('options[allowrenames]', 0);
$mform->hideIf('options[allowrenames]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_NEW);
$mform->hideIf('options[allowrenames]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_ALL);
$mform->addHelpButton('options[allowrenames]', 'allowrenames', 'tool_uploadcourse');
$mform->addElement('selectyesno', 'options[allowresets]', get_string('allowresets', 'tool_uploadcourse'));
$mform->setDefault('options[allowresets]', 0);
$mform->hideIf('options[allowresets]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_NEW);
$mform->hideIf('options[allowresets]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_ALL);
$mform->addHelpButton('options[allowresets]', 'allowresets', 'tool_uploadcourse');
}
}
File diff suppressed because it is too large Load Diff
+608
View File
@@ -0,0 +1,608 @@
<?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->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 {
/**
* 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 = core_text::strtoupper($repl);
break;
case '-':
$repl = core_text::strtolower($repl);
break;
case '~':
$repl = core_text::strtotitle($repl);
break;
}
if (!empty($block[2])) {
$repl = core_text::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 enrol_plugin[]
*/
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, but $CFG->keeptempdirectoriesonbackup
* needs to be enabled, otherwise the cache is ignored.
*
* @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);
if (empty($backupfile) || !is_readable($backupfile)) {
$errors['cannotreadbackupfile'] = new lang_string('cannotreadbackupfile', 'tool_uploadcourse');
return false;
}
$cachekey = 'backup_path:' . $backupfile;
} else if (!empty($shortname) || is_numeric($shortname)) {
$cachekey = 'backup_sn:' . $shortname;
}
if (empty($cachekey)) {
return false;
}
// If $CFG->keeptempdirectoriesonbackup is not set to true, any restore happening would
// automatically delete the backup directory... causing the cache to return an unexisting directory.
$usecache = !empty($CFG->keeptempdirectoriesonbackup);
if ($usecache) {
$cache = cache::make('tool_uploadcourse', 'helper');
}
// If we don't use the cache, or if we do and not set, or the directory doesn't exist any more.
if (!$usecache || (($backupid = $cache->get($cachekey)) === false || !is_dir(get_backup_temp_directory($backupid)))) {
// Use null instead of false because it would consider that the cache key has not been set.
$backupid = null;
if (!empty($backupfile)) {
// Extracting the backup file.
$packer = get_file_packer('application/vnd.moodle.backup');
$backupid = restore_controller::get_tempdir_name(SITEID, $USER->id);
$path = make_backup_temp_directory($backupid, false);
$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');
}
}
if ($usecache) {
$cache->set($cachekey, $backupid);
}
}
if ($backupid === null) {
$backupid = false;
}
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;
}
/**
* Helper to detect how many sections a course with a given shortname has.
*
* @param string $shortname shortname of a course to count sections from.
* @return integer count of sections.
*/
public static function get_coursesection_count($shortname) {
global $DB;
if (!empty($shortname) || is_numeric($shortname)) {
// Creating restore from an existing course.
$course = $DB->get_record('course', array('shortname' => $shortname));
}
if (!empty($course)) {
$courseformat = course_get_format($course);
return $courseformat->get_last_section_number();
}
return 0;
}
/**
* 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;
} else if (preg_match('/^(.+)?_role$/', $field, $matches)) {
if (!isset($rolesids[$value])) {
$invalidroles[] = $value;
break;
}
}
}
if (!empty($invalidroles)) {
$errors['invalidroles'] = new lang_string('invalidroles', 'tool_uploadcourse', implode(', ', $invalidroles));
}
// Roles names.
return $rolenames;
}
/**
* Return array of all custom course fields indexed by their shortname
*
* @return \core_customfield\field_controller[]
*/
public static function get_custom_course_fields(): array {
$result = [];
$fields = \core_course\customfield\course_handler::create()->get_fields();
foreach ($fields as $field) {
$result[$field->get('shortname')] = $field;
}
return $result;
}
/**
* Return array of custom field element names
*
* @return string[]
*/
public static function get_custom_course_field_names(): array {
$result = [];
$fields = self::get_custom_course_fields();
foreach ($fields as $field) {
$controller = \core_customfield\data_controller::create(0, null, $field);
$result[] = $controller->get_form_element_name();
}
return $result;
}
/**
* Return any elements from passed $data whose key matches one of the custom course fields defined for the site
*
* @param array $data
* @param array $defaults
* @param context $context
* @param array $errors Will be populated with any errors
* @return array
*/
public static function get_custom_course_field_data(array $data, array $defaults, context $context,
array &$errors = []): array {
$fields = self::get_custom_course_fields();
$result = [];
$canchangelockedfields = guess_if_creator_will_have_course_capability('moodle/course:changelockedcustomfields', $context);
foreach ($data as $name => $originalvalue) {
if (preg_match('/^customfield_(?<name>.*)?$/', $name, $matches)
&& isset($fields[$matches['name']])) {
$fieldname = $matches['name'];
$field = $fields[$fieldname];
// Skip field if it's locked and user doesn't have capability to change locked fields.
if ($field->get_configdata_property('locked') && !$canchangelockedfields) {
continue;
}
// Create field data controller.
$controller = \core_customfield\data_controller::create(0, null, $field);
$controller->set('id', 1);
$defaultvalue = $defaults["customfield_{$fieldname}"] ?? $controller->get_default_value();
$value = (empty($originalvalue) ? $defaultvalue : $field->parse_value($originalvalue));
// If we initially had a value, but now don't, then reset it to the default.
if (!empty($originalvalue) && empty($value)) {
$value = $defaultvalue;
}
// Validate data with controller.
$fieldformdata = [$controller->get_form_element_name() => $value];
$validationerrors = $controller->instance_form_validation($fieldformdata, []);
if (count($validationerrors) > 0) {
$errors['customfieldinvalid'] = new lang_string('customfieldinvalid', 'tool_uploadcourse',
$field->get_formatted_name());
continue;
}
$controller->set($controller->datafield(), $value);
// Pass an empty object to the data controller, which will transform it to a correct name/value pair.
$instance = new stdClass();
$controller->instance_form_before_set_data($instance);
$result = array_merge($result, (array) $instance);
}
}
return $result;
}
/**
* 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 = core_course_category::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);
if ($id && !core_course_category::get($id, IGNORE_MISSING)) {
// Category is not visible to the current user.
$id = false;
}
// 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);
if (!core_course_category::get($id, IGNORE_MISSING)) {
// Category is not visible to the current user.
$id = -1;
break;
}
$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,262 @@
<?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/>.
namespace tool_uploadcourse;
use context_course;
use context_coursecat;
use core_course_category;
use core_tag_tag;
use lang_string;
use tool_uploadcourse_course;
/**
* Checks various permissions related to the course upload process.
*
* @package tool_uploadcourse
* @copyright 2019 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class permissions {
/**
* Check permission to use tool_uploadcourse in a given category.
*
* @param int $catid
* @param lang_string|null $customerror
* @return lang_string|null
*/
protected static function check_permission_to_use_uploadcourse_tool(
int $catid,
?lang_string $customerror = null
): ?lang_string {
$category = core_course_category::get($catid, IGNORE_MISSING);
if (!$category || !has_capability('tool/uploadcourse:use', $category->get_context())) {
if ($customerror) {
return $customerror;
}
return new lang_string('courseuploadnotallowed', 'tool_uploadcourse',
$category ? $category->get_formatted_name() : $catid);
}
return null;
}
/**
* Check capabilities to delete a course and to use tool_uploadcourse for it.
*
* @param string $shortname course shortname
* @return lang_string|null
*/
public static function check_permission_to_delete(string $shortname): ?lang_string {
global $DB;
$course = $DB->get_record('course', ['shortname' => $shortname]);
if ($error = self::check_permission_to_use_uploadcourse_tool($course->category)) {
return $error;
}
if (!has_capability('moodle/course:delete', context_course::instance($course->id))) {
return new lang_string('nopermissions', 'error', get_capability_string('moodle/course:delete'));
}
return null;
}
/**
* Check capability in a course (that exists or is about to be created).
*
* @param int $do one of tool_uploadcourse_course::DO_UPDATE or tool_uploadcourse_course::DO_ADD
* @param array $coursedata data to update/create course with, must contain either 'id' or 'category' respectively
* @param string $capability capability to check
* @return lang_string|null error string or null
*/
protected static function check_capability(int $do, array $coursedata, string $capability): ?lang_string {
if ($do == tool_uploadcourse_course::DO_UPDATE) {
$context = context_course::instance($coursedata['id']);
$hascap = has_capability($capability, $context);
} else {
$catcontext = context_coursecat::instance($coursedata['category']);
$hascap = guess_if_creator_will_have_course_capability($capability, $catcontext);
}
if (!$hascap) {
return new lang_string('nopermissions', 'error', get_capability_string($capability));
}
return null;
}
/**
* Check permission to update the course.
*
* This checks capabilities:
* - to use tool_uploadcourse in the category where course is in and in the category where it will be moved to (if applicable).
* - to change course category (if applicable).
* - to update course details.
* - to force course language (if applicable).
* - to change course idnumber, shortname, fullname, summary, visibility, tags (if applicable).
*
* @param array $coursedata data to update a course with, always contains 'id'
* @return lang_string|null
*/
public static function check_permission_to_update(array $coursedata): ?lang_string {
$course = get_course($coursedata['id']);
if ($error = self::check_permission_to_use_uploadcourse_tool($course->category,
new lang_string('courseuploadupdatenotallowed', 'tool_uploadcourse'))) {
return $error;
}
if (!has_capability('moodle/course:update', context_course::instance($course->id))) {
return new lang_string('nopermissions', 'error', get_capability_string('moodle/course:update'));
}
// If user requested to change course category check permissions to use tool in target category
// and capabilities to change category.
if (!empty($coursedata['category']) && $coursedata['category'] != $course->category) {
if ($error = self::check_permission_to_use_uploadcourse_tool($coursedata['category'])) {
return $error;
}
if (!has_capability('moodle/course:changecategory', context_coursecat::instance($course->category))) {
return new lang_string('nopermissions', 'error', get_capability_string('moodle/course:changecategory'));
}
if (!has_capability('moodle/course:changecategory', context_coursecat::instance($coursedata['category']))) {
return new lang_string('nopermissions', 'error', get_capability_string('moodle/course:changecategory'));
}
}
$context = context_course::instance($coursedata['id']);
// If lang is specified, check the user is allowed to set that field.
if (!empty($coursedata['lang']) && $coursedata['lang'] !== $course->lang) {
if (!has_capability('moodle/course:setforcedlanguage', $context)) {
return new lang_string('cannotforcelang', 'tool_uploadcourse');
}
}
// Check permission to change course idnumber.
if (array_key_exists('idnumber', $coursedata) && $coursedata['idnumber'] !== $course->idnumber &&
!has_capability('moodle/course:changeidnumber', $context)) {
return new lang_string('nopermissions', 'error', get_capability_string('moodle/course:changeidnumber'));
}
// Check permission to change course shortname.
if (array_key_exists('shortname', $coursedata) && $coursedata['shortname'] !== $course->shortname &&
!has_capability('moodle/course:changeshortname', $context)) {
return new lang_string('nopermissions', 'error', get_capability_string('moodle/course:changeshortname'));
}
// Check permission to change course fullname.
if (array_key_exists('fullname', $coursedata) && $coursedata['fullname'] !== $course->fullname &&
!has_capability('moodle/course:changefullname', $context)) {
return new lang_string('nopermissions', 'error', get_capability_string('moodle/course:changefullname'));
}
// Check permission to change course summary.
if (array_key_exists('summary', $coursedata) && $coursedata['summary'] !== $course->summary &&
!has_capability('moodle/course:changesummary', $context)) {
return new lang_string('nopermissions', 'error', get_capability_string('moodle/course:changesummary'));
}
// Check permission to change course visibility.
if (array_key_exists('visible', $coursedata) && $coursedata['visible'] !== $course->visible &&
!has_capability('moodle/course:visibility', $context)) {
return new lang_string('nopermissions', 'error', get_capability_string('moodle/course:visibility'));
}
// If tags are specified and enabled check if user can updat them.
if (core_tag_tag::is_enabled('core', 'course') &&
(array_key_exists('tags', $coursedata) && strval($coursedata['tags']) !== '') &&
($error = self::check_capability(tool_uploadcourse_course::DO_UPDATE, $coursedata, 'moodle/course:tag'))) {
return $error;
}
return null;
}
/**
* Check permission to create course.
*
* This checks capabilities:
* - to use tool_uploadcourse in the category where course will be created.
* - to create a course.
* - to force course language (if applicable).
* - to set course tags (if applicable).
*
* @param array $coursedata data to create a course with, always contains 'category'
* @return lang_string|null
*/
public static function check_permission_to_create(array $coursedata): ?lang_string {
if ($error = self::check_permission_to_use_uploadcourse_tool($coursedata['category'])) {
return $error;
}
$catcontext = context_coursecat::instance($coursedata['category']);
// Check user is allowed to create courses in this category.
if (!has_capability('moodle/course:create', $catcontext)) {
return new lang_string('nopermissions', 'error', get_capability_string('moodle/course:create'));
}
// If lang is specified, check the user is allowed to set that field.
if (!empty($coursedata['lang'])) {
if (!guess_if_creator_will_have_course_capability('moodle/course:setforcedlanguage', $catcontext)) {
return new lang_string('cannotforcelang', 'tool_uploadcourse');
}
}
// Check permission to change course visibility.
if (array_key_exists('visible', $coursedata) && !$coursedata['visible'] &&
!guess_if_creator_will_have_course_capability('moodle/course:visibility', $catcontext)) {
return new lang_string('nopermissions', 'error', get_capability_string('moodle/course:visibility'));
}
// If tags are specified and enabled check if user can updat them.
if (core_tag_tag::is_enabled('core', 'course') &&
(array_key_exists('tags', $coursedata) && strval($coursedata['tags']) !== '') &&
($error = self::check_capability(tool_uploadcourse_course::DO_CREATE, $coursedata, 'moodle/course:tag'))) {
return $error;
}
return null;
}
/**
* Check if the user is able to reset a course.
*
* Capability to use the tool and update the course is already checked earlier.
*
* @param array $coursedata data to update course with, always contains 'id'
* @return lang_string|null error string or null
*/
public static function check_permission_to_reset(array $coursedata): ?lang_string {
return self::check_capability(tool_uploadcourse_course::DO_UPDATE, $coursedata, 'moodle/course:reset');
}
/**
* Check if the user is able to restore the mbz into a course.
*
* This method does not need to check if the course can be updated/created, this is checked earlier.
*
* @param int $do one of tool_uploadcourse_course::DO_UPDATE or tool_uploadcourse_course::DO_ADD
* @param array $coursedata data to update/create course with, must contain either 'id' or 'category' respectively
* @return lang_string|null error string or null
*/
public static function check_permission_to_restore(int $do, array $coursedata): ?lang_string {
return self::check_capability($do, $coursedata, 'moodle/restore:restorecourse');
}
}
@@ -0,0 +1,46 @@
<?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/>.
/**
* Privacy Subsystem implementation for tool_uploadcourse.
*
* @package tool_uploadcourse
* @copyright 2018 Zig Tan <zig@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_uploadcourse\privacy;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for tool_uploadcourse implementing null_provider.
*
* @copyright 2018 Zig Tan <zig@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
*
* @return string
*/
public static function get_reason(): string {
return 'privacy:metadata';
}
}
@@ -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 tool_uploadcourse_tracker $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;
// We will most certainly need extra time and memory to process big files.
core_php_time_limit::raise();
raise_memory_limit(MEMORY_EXTRA);
// 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);
if ($course->has_errors()) {
$errors++;
$tracker->output($this->linenb, false, $course->get_errors(), $data);
}
} else {
$errors++;
$tracker->output($this->linenb, false, $course->get_errors(), $data);
}
}
$tracker->finish();
$tracker->results($total, $created, $updated, $deleted, $errors);
}
/**
* 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->backuptempdir/...
*/
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 tool_uploadcourse_tracker $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();
// We might need extra time and memory depending on the number of rows to preview.
core_php_time_limit::raise();
raise_memory_limit(MEMORY_EXTRA);
// 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();
return $preview;
}
/**
* 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,82 @@
<?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('coursefile', 'tool_uploadcourse'));
$mform->addRule('coursefile', null, 'required');
$mform->addHelpButton('coursefile', 'coursefile', 'tool_uploadcourse');
$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');
}
$mform->addHelpButton('delimiter_name', 'csvdelimiter', 'tool_uploadcourse');
$choices = core_text::get_encodings();
$mform->addElement('select', 'encoding', get_string('encoding', 'tool_uploadcourse'), $choices);
$mform->setDefault('encoding', 'UTF-8');
$mform->addHelpButton('encoding', 'encoding', 'tool_uploadcourse');
$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);
$mform->addHelpButton('previewrows', 'rowpreviewnum', 'tool_uploadcourse');
$this->add_import_options();
$mform->addElement('hidden', 'showpreview', 1);
$mform->setType('showpreview', PARAM_INT);
$mform->addElement('hidden', 'categoryid');
$mform->setType('categoryid', PARAM_INT);
$this->add_action_buttons(false, get_string('preview', 'tool_uploadcourse'));
}
}
@@ -0,0 +1,298 @@
<?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();
require_once($CFG->dirroot . '/course/lib.php');
/**
* 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->hideIf('options[shortnametemplate]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE);
$mform->hideIf('options[shortnametemplate]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_UPDATE_ONLY);
// Restore file is not in the array options on purpose, because formslib can't handle it!
$contextid = $this->_customdata['contextid'];
$mform->addElement('hidden', 'contextid', $contextid);
$mform->setType('contextid', PARAM_INT);
$mform->addElement('filepicker', 'restorefile', get_string('templatefile', 'tool_uploadcourse'));
$mform->addHelpButton('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->hideIf('options[reset]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_NEW);
$mform->hideIf('options[reset]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_ALL);
$mform->disabledIf('options[reset]', 'options[allowresets]', 'eq', 0);
$mform->addHelpButton('options[reset]', 'reset', 'tool_uploadcourse');
// Default values.
$mform->addElement('header', 'defaultheader', get_string('defaultvalues', 'tool_uploadcourse'));
$mform->setExpanded('defaultheader', true);
$displaylist = core_course_category::make_categories_list('tool/uploadcourse:use');
$mform->addElement('autocomplete', 'defaults[category]', get_string('coursecategory'), $displaylist);
$mform->addRule('defaults[category]', null, 'required', null, 'client');
$mform->addHelpButton('defaults[category]', 'coursecategory');
$choices = array();
$choices['0'] = get_string('hide');
$choices['1'] = get_string('show');
$mform->addElement('select', 'defaults[visible]', get_string('coursevisibility'), $choices);
$mform->addHelpButton('defaults[visible]', 'coursevisibility');
$mform->setDefault('defaults[visible]', $courseconfig->visible);
if ($CFG->downloadcoursecontentallowed &&
has_capability('moodle/course:configuredownloadcontent', context::instance_by_id($contextid))) {
$downloadchoices = [
DOWNLOAD_COURSE_CONTENT_DISABLED => get_string('no'),
DOWNLOAD_COURSE_CONTENT_ENABLED => get_string('yes'),
];
$sitedefaultstring = $downloadchoices[$courseconfig->downloadcontentsitedefault];
$downloadchoices[DOWNLOAD_COURSE_CONTENT_SITE_DEFAULT] = get_string('sitedefaultspecified', '', $sitedefaultstring);
$downloadselectdefault = $courseconfig->downloadcontent ?? DOWNLOAD_COURSE_CONTENT_SITE_DEFAULT;
$mform->addElement('select', 'defaults[downloadcontent]', get_string('enabledownloadcoursecontent', 'course'),
$downloadchoices);
$mform->addHelpButton('defaults[downloadcontent]', 'downloadcoursecontent', 'course');
$mform->setDefault('defaults[downloadcontent]', $downloadselectdefault);
}
$mform->addElement('date_time_selector', 'defaults[startdate]', get_string('startdate'));
$mform->addHelpButton('defaults[startdate]', 'startdate');
$mform->setDefault('defaults[startdate]', time() + 3600 * 24);
$mform->addElement('date_time_selector', 'defaults[enddate]', get_string('enddate'), array('optional' => true));
$mform->addHelpButton('defaults[enddate]', 'enddate');
$courseformats = get_sorted_course_formats(true);
$formcourseformats = new core\output\choicelist();
$formcourseformats->set_allow_empty(false);
foreach ($courseformats as $courseformat) {
$definition = [];
$component = "format_$courseformat";
if (get_string_manager()->string_exists('plugin_description', $component)) {
$definition['description'] = get_string('plugin_description', $component);
}
$formcourseformats->add_option(
$courseformat,
get_string('pluginname', "format_$courseformat"),
[
'description' => $definition,
],
);
}
$mform->addElement(
'choicedropdown',
'defaults[format]',
get_string('format'),
$formcourseformats,
);
$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);
// Completion tracking.
if (!empty($CFG->enablecompletion)) {
$mform->addElement('selectyesno', 'defaults[enablecompletion]', get_string('enablecompletion', 'completion'));
$mform->setDefault('defaults[enablecompletion]', $courseconfig->enablecompletion);
$mform->addHelpButton('defaults[enablecompletion]', 'enablecompletion', 'completion');
}
$mform->addElement('selectyesno', 'defaults[showactivitydates]', get_string('showactivitydates'));
$mform->addHelpButton('defaults[showactivitydates]', 'showactivitydates');
$mform->setDefault('defaults[showactivitydates]', $courseconfig->showactivitydates);
// Add custom fields to the form.
$handler = \core_course\customfield\course_handler::create();
$handler->instance_form_definition($mform, 0, 'defaultvaluescustomfieldcategory', 'tool_uploadcourse');
// Hidden fields.
$mform->addElement('hidden', 'importid');
$mform->setType('importid', PARAM_INT);
$mform->addElement('hidden', 'previewrows');
$mform->setType('previewrows', PARAM_INT);
$mform->addElement('hidden', 'categoryid');
$mform->setType('categoryid', PARAM_INT);
$this->add_action_buttons(true, get_string('uploadcourses', 'tool_uploadcourse'));
// Prepare custom fields data.
$data = (object) $data;
$handler->instance_form_before_set_data($data);
$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');
}
/**
* Sets the enddate default after set_data is called.
*/
public function definition_after_data() {
$mform = $this->_form;
// The default end date depends on the course format.
$format = course_get_format((object)array('format' => get_config('moodlecourse', 'format')));
// Check if course end date form field should be enabled by default.
// If a default date is provided to the form element, it is magically enabled by default in the
// MoodleQuickForm_date_time_selector class, otherwise it's disabled by default.
if (get_config('moodlecourse', 'courseenddateenabled')) {
$enddate = $format->get_default_course_enddate($mform, array('startdate' => 'defaults[startdate]'));
$mform->setDefault('defaults[enddate]', $enddate);
}
// Tweak the form with values provided by custom fields in use.
\core_course\customfield\course_handler::create()->instance_form_definition_after_data($mform);
}
/**
* Validation.
*
* @param array $data
* @param array $files
* @return array the errors that were found
*/
public function validation($data, $files) {
global $DB;
$errors = parent::validation($data, $files);
if ($errorcode = course_validate_dates($data['defaults'])) {
$errors['defaults[enddate]'] = get_string($errorcode, 'error');
}
// Custom fields validation.
array_merge($errors, \core_course\customfield\course_handler::create()->instance_form_validation($data, $files));
return $errors;
}
}
+223
View File
@@ -0,0 +1,223 @@
<?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++));
// Ensure our data is suitable for HTML output.
echo html_writer::tag('td', isset($data['shortname']) ? s($data['shortname']) : '', array('class' => 'c' . $ci++));
echo html_writer::tag('td', isset($data['fullname']) ? s($data['fullname']) : '', array('class' => 'c' . $ci++));
echo html_writer::tag('td', isset($data['idnumber']) ? s($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,202 @@
<?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 . '/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' => core_course_category::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 --mode=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 = core_text::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['enddate'] = $defaults['startdate'] + intval(get_config('moodlecourse', 'courseduration'));
$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;
$defaults['enablecompletion'] = $courseconfig->enablecompletion;
$defaults['showactivitydates'] = $courseconfig->showactivitydates;
// 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.
\core\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) {
throw new \moodle_exception('csvfileerror', 'tool_uploadcourse', '', $cir->get_error());
} else if ($readcount == 0) {
throw new \moodle_exception('csvemptyfile', 'error', '', $cir->get_error());
}
$processor = new tool_uploadcourse_processor($cir, $processoroptions, $defaults);
$processor->execute(new tool_uploadcourse_tracker(tool_uploadcourse_tracker::OUTPUT_PLAIN));
+36
View File
@@ -0,0 +1,36 @@
<?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/>.
/**
* Capability definitions for tool_uploadcourse.
*
* @package tool_uploadcourse
* @copyright 2019 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$capabilities = [
'tool/uploadcourse:use' => [
'riskbitmask' => RISK_SPAM,
'captype' => 'write',
'contextlevel' => CONTEXT_COURSECAT,
'archetypes' => [
'manager' => CAP_ALLOW,
],
],
];
+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,
)
);
+144
View File
@@ -0,0 +1,144 @@
<?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 . '/csvlib.class.php');
$importid = optional_param('importid', '', PARAM_INT);
$categoryid = optional_param('categoryid', 0, PARAM_INT);
$previewrows = optional_param('previewrows', 10, PARAM_INT);
$returnurl = new moodle_url('/admin/tool/uploadcourse/index.php');
if ($categoryid) {
// When categoryid is specified, setup the page for this category and check capability in its context.
require_login(null, false);
$category = core_course_category::get($categoryid);
$categoryname = isset($category) ? $category->get_formatted_name() : $SITE->fullname;
$context = context_coursecat::instance($categoryid);
require_capability('tool/uploadcourse:use', $context);
$PAGE->set_context($context);
$PAGE->set_url(new moodle_url('/admin/tool/uploadcourse/index.php', ['categoryid' => $categoryid]));
$PAGE->set_pagelayout('admin');
$PAGE->set_title("$categoryname: " . get_string('uploadcourses', 'tool_uploadcourse'));
$PAGE->set_heading($categoryname);
} else {
admin_externalpage_setup('tooluploadcourse');
}
if (empty($importid)) {
$mform1 = new tool_uploadcourse_step1_form();
$mform1->set_data(['categoryid' => $categoryid]);
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) {
throw new \moodle_exception('csvfileerror', 'tool_uploadcourse', $returnurl, $cir->get_error());
} else if ($readcount == 0) {
throw new \moodle_exception('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.
$categorydefaults = $categoryid ? ['category' => $categoryid] : [];
$data = ['importid' => $importid, 'previewrows' => $previewrows, 'categoryid' => $categoryid, 'defaults' => $categorydefaults];
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;
// Custom field defaults.
$customfields = tool_uploadcourse_helper::get_custom_course_field_names();
foreach ($customfields as $customfield) {
$defaults[$customfield] = $form2data->{$customfield};
}
// Restorefile deserves its own logic because formslib does not really appreciate
// when the name of a filepicker is an array...
$options['restorefile'] = '';
if (!empty($form2data->restorefile)) {
$options['restorefile'] = $mform2->save_temp_file('restorefile');
}
$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);
}
// Deleting the file after processing or preview.
if (!empty($options['restorefile'])) {
@unlink($options['restorefile']);
}
} else {
if (!empty($form1data)) {
$options = $form1data->options;
} else if ($submitteddata = $mform2->get_submitted_data()) {
$options = (array)$submitteddata->options;
} else {
// Weird but we still need to provide a value, setting the default step1_form one.
$options = array('mode' => tool_uploadcourse_processor::MODE_CREATE_NEW);
}
$processor = new tool_uploadcourse_processor($cir, $options, $categorydefaults);
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,143 @@
<?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['allowdeletes_help'] = 'Whether the delete field is accepted or not.';
$string['allowrenames'] = 'Allow renames';
$string['allowrenames_help'] = 'Whether the rename field is accepted or not.';
$string['allowresets'] = 'Allow resets';
$string['allowresets_help'] = 'Whether the reset field is accepted or not.';
$string['cachedef_helper'] = 'Helper caching';
$string['cannotdeletecoursenotexist'] = 'Cannot delete a course that does not exist';
$string['cannotforcelang'] = 'No permission to force language for this course';
$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['cannotupdatefrontpage'] = 'You are not allowed to change the site home.';
$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['coursefile'] = 'File';
$string['coursefile_help'] = 'This file must be a CSV file.';
$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['courseuploadnotallowed'] = 'No permission to upload courses in category: {$a}';
$string['courseuploadupdatenotallowed'] = 'A course with this short name exists but you don\'t have permission to use the upload courses functionality to update it.';
$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 separator';
$string['csvdelimiter_help'] = 'The character separating the series of data in each record.';
$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 separator and file encoding are correct. {$a}';
$string['csvline'] = 'Line';
$string['defaultvalues'] = 'Default course values';
$string['defaultvaluescustomfieldcategory'] = 'Default values for \'{$a}\'';
$string['downloadcontentnotallowed'] = 'Configuring download of course content not allowed';
$string['encoding'] = 'Encoding';
$string['encoding_help'] = 'Encoding of the CSV file.';
$string['errorcannotcreateorupdateenrolment'] = 'Cannot create or update enrolment method \'{$a}\'';
$string['errorcannotdeleteenrolment'] = 'Cannot delete enrolment method \'{$a}\'';
$string['errorcannotdisableenrolment'] = 'Cannot disable enrolment method \'{$a}\'';
$string['errorwhilerestoringcourse'] = 'Error while restoring the course';
$string['errorwhiledeletingcourse'] = 'Error while deleting the course';
$string['errorunsupportedmethod'] = 'Enrolment method \'{$a}\' is not supported in csv upload';
$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['invaliddownloadcontent'] = 'Invalid download of course content value';
$string['invalidencoding'] = 'Invalid encoding';
$string['invalidmode'] = 'Invalid mode selected';
$string['invalideupdatemode'] = 'Invalid update mode selected';
$string['invalidvisibilitymode'] = 'Invalid visible mode';
$string['invalidroles'] = 'Invalid role names: {$a}';
$string['invalidshortname'] = 'Invalid shortname';
$string['invalidfullnametoolong'] = 'The fullname field is limited to {$a} characters';
$string['invalidshortnametoolong'] = 'The shortname field is limited to {$a} characters';
$string['missingmandatoryfields'] = 'Missing value for mandatory fields: {$a}';
$string['missingshortnamenotemplate'] = 'Missing shortname and shortname template not set';
$string['mode'] = 'Upload mode';
$string['mode_help'] = 'This allows you to specify if courses can be created and/or updated.';
$string['nochanges'] = 'No changes';
$string['pluginname'] = 'Course upload';
$string['preview'] = 'Preview';
$string['customfieldinvalid'] = 'Custom field \'{$a}\' is empty or contains invalid data';
$string['reset'] = 'Reset course after upload';
$string['reset_help'] = 'Whether to reset the course after creating/updating it.';
$string['result'] = 'Result';
$string['restoreafterimport'] = 'Restore after import';
$string['rowpreviewnum'] = 'Preview rows';
$string['rowpreviewnum_help'] = 'Number of rows from the CSV file that will be previewed on the following page. This option is for limiting the size of the following page.';
$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 items from CSV data and defaults';
$string['updatemode'] = 'Update mode';
$string['updatemode_help'] = 'If you allow courses to be updated, you also have to tell the tool what to update the courses with.';
$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['uploadcourse:use'] = 'Use upload course tool';
$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 the selected separator
* The first record contains a list of fieldnames defining the format of the rest of the file
* Required fieldnames are shortname, fullname, and category';
$string['uploadcoursespreview'] = 'Upload courses preview';
$string['uploadcoursesresult'] = 'Upload courses results';
$string['privacy:metadata'] = 'The Course upload plugin does not store any personal data.';
+44
View File
@@ -0,0 +1,44 @@
<?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 callbacks for tool_uploadcourse.
*
* @package tool_uploadcourse
* @copyright 2019 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Extends the navigation of the category admin menu with the upload courses link.
*
* @param navigation_node $navigation The navigation node to extend
* @param context $coursecategorycontext The context of the course category
*/
function tool_uploadcourse_extend_navigation_category_settings(navigation_node $navigation, context $coursecategorycontext): void {
if (has_capability('tool/uploadcourse:use', $coursecategorycontext)) {
$title = get_string('uploadcourses', 'tool_uploadcourse');
$path = new moodle_url('/admin/tool/uploadcourse/index.php', ['categoryid' => $coursecategorycontext->instanceid]);
$settingsnode = navigation_node::create(
$title,
$path,
navigation_node::TYPE_SETTING,
null,
null,
new pix_icon('i/course', ''));
$navigation->add_node($settingsnode);
}
}
+33
View File
@@ -0,0 +1,33 @@
<?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();
$ADMIN->add('courses',
new admin_externalpage('tooluploadcourse',
get_string('uploadcourses', 'tool_uploadcourse'),
"$CFG->wwwroot/$CFG->admin/tool/uploadcourse/index.php",
'tool/uploadcourse:use'
)
);
@@ -0,0 +1,128 @@
@tool @tool_uploadcourse @_file_upload
Feature: An admin can create courses with cohort enrolments using a CSV file
In order to create courses using a CSV file with cohort enrolment
As an admin
I need to be able to upload a CSV file and navigate through the import process
Background:
Given the following "categories" exist:
| name | category | idnumber |
| Cat 0 | 0 | CAT0 |
| Cat 1 | CAT0 | CAT1 |
| Cat 1 | CAT0 | CAT2 |
And the following "cohorts" exist:
| name | idnumber | contextlevel | reference | visible |
| Cohort 1 | CV1 | Category | CAT1 | 1 |
| Cohort 2 | CV2 | Category | CAT2 | 1 |
| Cohort 3 | CV3 | Category | CAT2 | 1 |
| Cohort 4 | CV4 | Category | CAT1 | 1 |
| Cohort 5 | CV5 | Category | CAT1 | 1 |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | CAT1 |
And I log in as "admin"
And I navigate to "Courses > Upload courses" in site administration
And I set the field "Upload mode" to "Create new courses, or update existing ones"
And I set the field "Update mode" to "Update with CSV data only"
@javascript
Scenario: Upload cohort enrolment if plugin is disabled
Given the following config values are set as admin:
| enrol_plugins_enabled | manual,guest,self |
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_cohort.csv" file to "File" filemanager
When I click on "Preview" "button"
Then I should see "Cohort sync enrol plugin is disabled"
@javascript
Scenario: Validation of cohorts for uploaded courses
Given I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_cohort.csv" file to "File" filemanager
And I click on "Preview" "button"
And I should see "Unknown cohort (Not exist)!"
And I should see "Cohort CV3 not allowed in this context."
When I click on "Upload courses" "button"
And I should see "Unknown cohort (Not exist)!"
And I should see "Cohort CV3 not allowed in this context."
And I should see "Cohort CV4 not allowed in this context."
And I should see "Invalid role names: student1"
And I should see "Courses created: 2"
And I should see "Courses updated: 0"
And I should see "Courses errors: 4"
And I am on the "Course 1" "enrolment methods" page
Then I should not see "Cohort sync (Cohort 3 - Student)"
And I am on the "Course 2" "enrolment methods" page
And I should not see "Cohort sync (Cohort 4 - Student)"
And I am on the "Course 3" "enrolment methods" page
And I should see "Cohort sync (Cohort 5 - Student)"
And I click on "Edit" "link" in the "Cohort 5" "table_row"
And the field "Add to group" matches value "None"
And I navigate to "Courses > Upload courses" in site administration
And I set the field "Upload mode" to "Create new courses, or update existing ones"
And I set the field "Update mode" to "Update with CSV data only"
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_cohort_missing_fields.csv" file to "File" filemanager
And I click on "Preview" "button"
And I should see "Missing value for mandatory fields: cohortidnumber, role"
And "Upload courses" "button" should exist
@javascript
Scenario: Validation of groups for uploaded courses with cohort enrolments
Given the following "groups" exist:
| name | course | idnumber |
| group1 | C1 | G1 |
# Test that groupname can't be set when addtogroup is used.
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_cohort_addtogroup_groupname.csv" file to "File" filemanager
And I click on "Preview" "button"
And I should see "You cannot specify groupname when addtogroup is set."
# Test creating a new group when uploading a course.
And I navigate to "Courses > Upload courses" in site administration
And I set the field "Upload mode" to "Create new courses, or update existing ones"
And I set the field "Update mode" to "Update with CSV data only"
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_cohort_addtogroup.csv" file to "File" filemanager
And I click on "Preview" "button"
And I click on "Upload courses" "button"
And I should see "Courses created: 2"
And I should see "Courses errors: 0"
And I am on the "Course 2" "enrolment methods" page
And I should see "Cohort sync (Cohort 2 - Student)"
And I click on "Edit" "link" in the "Cohort 2" "table_row"
And the field "Add to group" matches value "Cohort 2 cohort"
And I am on the "Course 2" "groups" page
And I should see "Cohort 2 cohort"
And I am on the "Course 3" "enrolment methods" page
And I should see "Cohort sync (Cohort 1 - Student)"
And I click on "Edit" "link" in the "Cohort 1" "table_row"
And the field "Add to group" matches value "None"
And I am on the "Course 3" "groups" page
And I should not see "Cohort 1 cohort"
# Test assigning to an existing group when uploading a course.
And I navigate to "Courses > Upload courses" in site administration
And I set the field "Upload mode" to "Create new courses, or update existing ones"
And I set the field "Update mode" to "Update with CSV data only"
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_cohort_groups.csv" file to "File" filemanager
And I click on "Preview" "button"
And I should see "Error, invalid group notexist"
When I click on "Upload courses" "button"
And I should see "Error, invalid group notexist"
And I should see "Courses updated: 1"
And I should see "Courses errors: 1"
And I am on the "Course 1" "enrolment methods" page
Then I should see "Cohort sync (Cohort 1 - Student)"
And I click on "Edit" "link" in the "Cohort 1" "table_row"
And the field "Add to group" matches value "group1"
And I am on the "Course 1" "groups" page
And I should see "group1"
@javascript
Scenario: Upload multiple enrolment methods of same type to the same course
Given I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_cohort_multiple.csv" file to "File" filemanager
And I click on "Preview" "button"
When I click on "Upload courses" "button"
And I should see "Courses updated: 2"
And I am on the "Course 1" "enrolment methods" page
Then I should see "Cohort sync (Cohort 1 - Student)"
And I should see "Cohort sync (Cohort 4 - Non-editing teacher)"
And I click on "Edit" "link" in the "Cohort 1" "table_row"
And the field "Assign role" matches value "Student"
And I press "Cancel"
And I click on "Edit" "link" in the "Cohort 4" "table_row"
And the field "Assign role" matches value "Non-editing teacher"
@@ -0,0 +1,183 @@
@tool @tool_uploadcourse @_file_upload
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" exist:
| fullname | shortname | category |
| First course | C1 | 0 |
And I log in as "admin"
And I navigate to "Courses > Upload courses" in site administration
@javascript
Scenario: Creation of unexisting courses
Given I upload "admin/tool/uploadcourse/tests/fixtures/courses.csv" file to "File" filemanager
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 am on site homepage
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" filemanager
And I set the field "Upload mode" to "Create all, increment shortname if needed"
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 am on site homepage
And I should see "Course 1"
And I should see "Course 2"
And I should see "Course 3"
@javascript
Scenario: Creation of new courses with custom fields
Given the following "custom field categories" exist:
| name | component | area | itemid |
| Other | core_course | course | 0 |
And the following "custom fields" exist:
| name | category | type | shortname | configdata |
| Field 1 | Other | checkbox | checkbox | |
| Field 2 | Other | date | date | |
| Field 3 | Other | select | select | {"options":"a\nb\nc"} |
| Field 4 | Other | text | text | |
| Field 5 | Other | textarea | textarea | |
When I upload "admin/tool/uploadcourse/tests/fixtures/courses_custom_fields.csv" file to "File" filemanager
And I set the field "Upload mode" to "Create new courses only, skip existing ones"
And I click on "Preview" "button"
And I click on "Upload courses" "button"
Then I should see "Course created"
And I should see "Courses created: 1"
And I am on site homepage
And I should see "Course fields 1"
And I should see "Field 1: Yes"
And I should see "Field 2: Tuesday, 1 October 2019, 2:00"
And I should see "Field 3: b"
And I should see "Field 4: Hello"
And I should see "Field 5: Goodbye"
@javascript
Scenario: Creation of new courses with custom fields using defaults
Given the following "custom field categories" exist:
| name | component | area | itemid |
| Other | core_course | course | 0 |
And the following "custom fields" exist:
| name | category | type | shortname | configdata |
| Field 1 | Other | checkbox | checkbox | {"checkbydefault":1} |
| Field 2 | Other | date | date | {"includetime":0} |
| Field 3 | Other | select | select | {"options":"a\nb\nc","defaultvalue":"b"} |
| Field 4 | Other | text | text | {"defaultvalue":"Hello"} |
| Field 5 | Other | textarea | textarea | {"defaultvalue":"Some text","defaultvalueformat":1} |
When I upload "admin/tool/uploadcourse/tests/fixtures/courses.csv" file to "File" filemanager
And I set the field "Upload mode" to "Create all, increment shortname if needed"
And I click on "Preview" "button"
And I expand all fieldsets
And the field "Field 1" matches value "1"
And the field "Field 3" matches value "b"
And the field "Field 4" matches value "Hello"
And the field "Field 5" matches value "Some text"
# We have to enable the date field manually.
And I set the following fields to these values:
| customfield_date[enabled] | 1 |
| customfield_date[day] | 1 |
| customfield_date[month] | June |
| customfield_date[year] | 2020 |
And I click on "Upload courses" "button"
Then I should see "Course created"
And I should see "Courses created: 3"
And I am on site homepage
And I should see "Course 1"
And I should see "Field 1: Yes"
And I should see "Field 2: 1 June 2020"
And I should see "Field 3: b"
And I should see "Field 4: Hello"
And I should see "Field 5: Some text"
@javascript
Scenario: Validation of role for uploaded courses
Given I navigate to "Users > Permissions > Define roles" in site administration
And I click on "Add a new role" "button"
And I click on "Continue" "button"
And I set the following fields to these values:
| Short name | notallowed |
| Custom full name | notallowed |
| contextlevel80 | 1 |
And I click on "Create this role" "button"
And I navigate to "Courses > Upload courses" in site administration
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_role.csv" file to "File" filemanager
And I click on "Preview" "button"
And I should see "Invalid role names: notexist"
And I should see "Role notallowed not allowed in this context."
When I click on "Upload courses" "button"
And I should see "Course created"
And I should see "Courses total: 3"
And I should see "Courses created: 1"
And I should see "Courses errors: 2"
And I should see "Invalid role names: notexist"
And I should see "Role notallowed not allowed in this context."
And I am on site homepage
And I should see "coursez"
@javascript
Scenario: Unsupported enrol methods are not created
Given the following config values are set as admin:
| enrol_plugins_enabled | manual,guest,lti |
And I set the field "Upload mode" to "Create new courses, or update existing ones"
And I set the field "Update mode" to "Update with CSV data only"
And I upload "admin/tool/uploadcourse/tests/fixtures/unsupported_enrol_method.csv" file to "File" filemanager
And I click on "Preview" "button"
When I click on "Upload courses" "button"
Then I should see "Course created"
And I should see "Enrolment method 'enrol_lti_plugin' is not supported in csv upload"
And I am on the "C2" "enrolment methods" page
And I should see "manualtest"
And I should not see "ltitest"
@javascript
Scenario: Manager can use upload course tool in course category
Given the following "users" exist:
| username | firstname | lastname | email |
| user1 | User | 1 | user1@example.com |
And the following "categories" exist:
| name | category | idnumber |
| Cat 1 | 0 | CAT1 |
| Cat 2 | 0 | CAT2 |
| Cat 3 | CAT1 | CAT3 |
And the following "role assigns" exist:
| user | role | contextlevel | reference |
| user1 | manager | Category | CAT1 |
When I log in as "user1"
And I am on course index
And I follow "Cat 1"
And I navigate to "Upload courses" in current page administration
And I upload "admin/tool/uploadcourse/tests/fixtures/courses_manager1.csv" file to "File" filemanager
And I click on "Preview" "button"
Then I should see "The course exists and update is not allowed" in the "C1" "table_row"
And I should see "No permission to upload courses in category: Cat 2" in the "C2" "table_row"
And I set the field "Course category" to "Cat 1 / Cat 3"
And I click on "Upload courses" "button"
And I should see "Course created"
And I should see "Courses total: 5"
And I should see "Courses created: 3"
And I should see "Courses errors: 2"
And I am on course index
And I follow "Cat 1"
And I should see "Course 4"
And I follow "Cat 3"
And I should see "Course 5"
# Course 3 did not have category specified in CSV file and it was uploaded to the current category.
And I should see "Course 3"
And I log out
@@ -0,0 +1,156 @@
@tool @tool_uploadcourse @_file_upload
Feature: An admin can update courses enrolments using a CSV file
In order to update courses enrolments using a CSV file
As an admin
I need to be able to upload a CSV file with enrolment methods for the courses
Background:
Given the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And I log in as "admin"
@javascript
Scenario: Creating enrolment method by enable it
Given I am on the "Course 1" "enrolment methods" page
And I click on "Delete" "link" in the "Guest access" "table_row"
And I click on "Continue" "button"
And I should not see "Guest access" in the "generaltable" "table"
And I navigate to "Courses > Upload courses" in site administration
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_enable.csv" file to "File" filemanager
And I set the field "Upload mode" to "Only update existing courses"
And I set the field "Update mode" to "Update with CSV data only"
And I set the field "Allow deletes" to "Yes"
And I click on "Preview" "button"
When I click on "Upload courses" "button"
Then I should see "Course updated"
And I am on the "Course 1" "enrolment methods" page
And "Disable" "icon" should exist in the "Guest access" "table_row"
@javascript
Scenario: Creating enrolment method by disabling it
Given I am on the "Course 1" "enrolment methods" page
And I click on "Delete" "link" in the "Guest access" "table_row"
And I click on "Continue" "button"
And I should not see "Guest access" in the "generaltable" "table"
And I navigate to "Courses > Upload courses" in site administration
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_disable.csv" file to "File" filemanager
And I set the field "Upload mode" to "Only update existing courses"
And I set the field "Update mode" to "Update with CSV data only"
And I set the field "Allow deletes" to "Yes"
And I click on "Preview" "button"
When I click on "Upload courses" "button"
Then I should see "Course updated"
And I am on the "Course 1" "enrolment methods" page
And "Enable" "icon" should exist in the "Guest access" "table_row"
@javascript
Scenario: Enabling enrolment method
Given I navigate to "Courses > Upload courses" in site administration
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_enable.csv" file to "File" filemanager
And I set the field "Upload mode" to "Only update existing courses"
And I set the field "Update mode" to "Update with CSV data only"
And I set the field "Allow deletes" to "Yes"
And I click on "Preview" "button"
When I click on "Upload courses" "button"
Then I should see "Course updated"
And I am on the "Course 1" "enrolment methods" page
And "Disable" "icon" should exist in the "Guest access" "table_row"
@javascript
Scenario: Disable an enrolment method
Given I am on the "Course 1" "enrolment methods" page
And I click on "Enable" "link" in the "Guest access" "table_row"
And "Disable" "icon" should exist in the "Guest access" "table_row"
And I navigate to "Courses > Upload courses" in site administration
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_disable.csv" file to "File" filemanager
And I set the field "Upload mode" to "Only update existing courses"
And I set the field "Update mode" to "Update with CSV data only"
And I set the field "Allow deletes" to "Yes"
And I click on "Preview" "button"
When I click on "Upload courses" "button"
Then I should see "Course updated"
And I am on the "Course 1" "enrolment methods" page
And "Enable" "icon" should exist in the "Guest access" "table_row"
@javascript
Scenario: Delete an enrolment method
Given I navigate to "Courses > Upload courses" in site administration
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_delete.csv" file to "File" filemanager
And I set the field "Upload mode" to "Only update existing courses"
And I set the field "Update mode" to "Update with CSV data only"
And I set the field "Allow deletes" to "Yes"
And I click on "Preview" "button"
When I click on "Upload courses" "button"
Then I should see "Course updated"
And I am on the "Course 1" "enrolment methods" page
And I should not see "Guest access" in the "generaltable" "table"
@javascript
Scenario: Delete an unexistent enrolment method (nothing should change)
Given I am on the "Course 1" "enrolment methods" page
And I click on "Delete" "link" in the "Guest access" "table_row"
And I click on "Continue" "button"
And I should not see "Guest access" in the "generaltable" "table"
And I navigate to "Courses > Upload courses" in site administration
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_delete.csv" file to "File" filemanager
And I set the field "Upload mode" to "Only update existing courses"
And I set the field "Update mode" to "Update with CSV data only"
And I set the field "Allow deletes" to "Yes"
And I click on "Preview" "button"
When I click on "Upload courses" "button"
Then I should see "Course updated"
And I am on the "Course 1" "enrolment methods" page
And I should not see "Guest access" in the "generaltable" "table"
@javascript
Scenario: Re-upload a file using CSV data only after deleting the enrolments method
Given I navigate to "Plugins > Enrolments > Manage enrol plugins" in site administration
And I click on "Enable" "link" in the "Course meta link" "table_row"
And the following "cohort" exists:
| name | Cohort1 |
| idnumber | Cohort1 |
And the following "category" exists:
| name | Cat 1 |
| category | 0 |
| idnumber | CAT1 |
And I navigate to "Courses > Upload courses" in site administration
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_multiple.csv" file to "File" filemanager
And I click on "Preview" "button"
And I click on "Upload courses" "button"
And I am on the "Course 2" "enrolment methods" page
And I should see "Self enrolment (Student)" in the "generaltable" "table"
And I should see "Cohort sync (Cohort1 - Student)" in the "generaltable" "table"
And I should see "Guest access" in the "generaltable" "table"
And I should see "Manual enrolments" in the "generaltable" "table"
And I should see "Course meta link (Course 1)" in the "generaltable" "table"
And I navigate to "Courses > Upload courses" in site administration
# Delete all enrolment methods.
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_multiple_delete.csv" file to "File" filemanager
And I set the field "Upload mode" to "Only update existing courses"
And I set the field "Update mode" to "Update with CSV data only"
And I set the field "Allow deletes" to "Yes"
And I click on "Preview" "button"
And I click on "Upload courses" "button"
And I should see "Course updated"
And I am on the "Course 2" "enrolment methods" page
And I should not see "Self enrolment (Student)" in the "generaltable" "table"
And I should not see "Cohort sync (Cohort1 - Student)" in the "generaltable" "table"
And I should not see "Guest access" in the "generaltable" "table"
And I should not see "Manual enrolments" in the "generaltable" "table"
And I should not see "Course meta link (Course 1)" in the "generaltable" "table"
# Re-upload again the CSV file, to add again the enrolment methods.
And I navigate to "Courses > Upload courses" in site administration
When I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_multiple.csv" file to "File" filemanager
And I set the field "Upload mode" to "Only update existing courses"
And I set the field "Update mode" to "Update with CSV data only"
And I set the field "Allow deletes" to "Yes"
And I click on "Preview" "button"
And I click on "Upload courses" "button"
And I am on the "Course 2" "enrolment methods" page
Then I should see "Self enrolment (Student)" in the "generaltable" "table"
And I should see "Cohort sync (Cohort1 - Student)" in the "generaltable" "table"
And I should see "Guest access" in the "generaltable" "table"
And I should see "Manual enrolments" in the "generaltable" "table"
And I should see "Course meta link (Course 1)" in the "generaltable" "table"
And I navigate to "Courses > Upload courses" in site administration
@@ -0,0 +1,104 @@
@tool @tool_uploadcourse @_file_upload
Feature: An admin can create courses with guest enrolments using a CSV file
In order to create courses using a CSV file with guest enrolment
As an admin
I need to be able to upload a CSV file and navigate through the import process
Background:
Given the following "categories" exist:
| name | category | idnumber |
| Cat 0 | 0 | CAT0 |
| Cat 1 | CAT0 | CAT1 |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | CAT1 |
And I log in as "admin"
And I navigate to "Courses > Upload courses" in site administration
And I set the field "Upload mode" to "Create new courses, or update existing ones"
And I set the field "Update mode" to "Update with CSV data only"
@javascript
Scenario: Validation of password for uploaded courses with guest enrolments
# usepasswordpolicy is not set.
Given I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_guest.csv" file to "File" filemanager
And I click on "Preview" "button"
And I click on "Upload courses" "button"
And I should see "Courses created: 2"
And I should see "Courses updated: 1"
And I should see "Courses errors: 0"
And I am on the "Course 1" "enrolment methods" page
And I click on "Edit" "link" in the "Guest access" "table_row"
And the field "Password" matches value "test"
And I press "Cancel"
And I click on "Delete" "link" in the "Guest access" "table_row"
And I press "Continue"
And I am on the "Course 2" "enrolment methods" page
And I click on "Edit" "link" in the "Guest access" "table_row"
And the field "Password" matches value ""
And I press "Cancel"
And I click on "Delete" "link" in the "Guest access" "table_row"
And I press "Continue"
And I am on the "Course 3" "enrolment methods" page
And I click on "Edit" "link" in the "Guest access" "table_row"
And the field "Password" matches value "Test123@"
And I press "Cancel"
And I click on "Delete" "link" in the "Guest access" "table_row"
And I press "Continue"
# Policy is used, but password not required so it will not be generated if omitted.
And the following config values are set as admin:
| config | value | plugin |
| usepasswordpolicy | 1 | enrol_guest |
And I navigate to "Courses > Upload courses" in site administration
And I set the field "Upload mode" to "Create new courses, or update existing ones"
And I set the field "Update mode" to "Update with CSV data only"
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_guest.csv" file to "File" filemanager
And I click on "Preview" "button"
And I should see "Passwords must be at least 8 characters long."
And I should see "Passwords must have at least 1 digit(s)."
And I should see "Passwords must have at least 1 upper case letter(s)."
And I should see "The password must have at least 1 special character(s) such as *, -, or #."
And I click on "Upload courses" "button"
And I should see "Courses created: 0"
And I should see "Courses updated: 2"
And I should see "Courses errors: 1"
And I am on the "Course 1" "enrolment methods" page
And "Guest access" "table_row" should not exist
And I am on the "Course 2" "enrolment methods" page
And I click on "Edit" "link" in the "Guest access" "table_row"
And the field "Password" matches value ""
And I press "Cancel"
And I click on "Delete" "link" in the "Guest access" "table_row"
And I press "Continue"
And I am on the "Course 3" "enrolment methods" page
And I click on "Edit" "link" in the "Guest access" "table_row"
And the field "Password" matches value "Test123@"
And I press "Cancel"
And I click on "Delete" "link" in the "Guest access" "table_row"
And I press "Continue"
# Policy is used and password not required so it will be generated if omitted.
And the following config values are set as admin:
| config | value | plugin |
| requirepassword | 1 | enrol_guest |
And I navigate to "Courses > Upload courses" in site administration
And I set the field "Upload mode" to "Create new courses, or update existing ones"
And I set the field "Update mode" to "Update with CSV data only"
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_guest.csv" file to "File" filemanager
And I click on "Preview" "button"
And I should see "Passwords must be at least 8 characters long."
And I should see "Passwords must have at least 1 digit(s)."
And I should see "Passwords must have at least 1 upper case letter(s)."
And I should see "The password must have at least 1 special character(s) such as *, -, or #."
And I click on "Upload courses" "button"
And I should see "Courses created: 0"
And I should see "Courses updated: 2"
And I should see "Courses errors: 1"
And I am on the "Course 1" "enrolment methods" page
And "Guest access" "table_row" should not exist
And I am on the "Course 2" "enrolment methods" page
And I click on "Edit" "link" in the "Guest access" "table_row"
And the field "Password" does not match value ""
And I am on the "Course 3" "enrolment methods" page
And I click on "Edit" "link" in the "Guest access" "table_row"
And the field "Password" matches value "Test123@"
@@ -0,0 +1,88 @@
@tool @tool_uploadcourse @_file_upload
Feature: An admin can create courses with meta enrolments using a CSV file
In order to create courses using a CSV file with meta enrolment
As an admin
I need to be able to upload a CSV file and navigate through the import process
Background:
Given the following "categories" exist:
| name | category | idnumber |
| Cat 0 | 0 | CAT0 |
| Cat 1 | CAT0 | CAT1 |
| Cat 1 | CAT0 | CAT2 |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | CAT1 |
| Course 2 | C2 | CAT1 |
And I log in as "admin"
And I navigate to "Plugins > Enrolments > Manage enrol plugins" in site administration
And I click on "Enable" "link" in the "Course meta link" "table_row"
And I navigate to "Courses > Upload courses" in site administration
And I set the field "Upload mode" to "Create new courses, or update existing ones"
And I set the field "Update mode" to "Update with CSV data only"
@javascript
Scenario: Validation of meta link course shortname for uploaded courses
Given I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_meta.csv" file to "File" filemanager
And I click on "Preview" "button"
And I should see "Unknown meta course shortname"
And I should see "You can't add a meta link to the same course."
When I click on "Upload courses" "button"
And I should see "Unknown meta course shortname"
And I should see "You can't add a meta link to the same course."
And I should see "Courses created: 1"
And I should see "Courses updated: 0"
And I should see "Courses errors: 2"
And I am on the "Course 4" "enrolment methods" page
Then I should see "Course meta link (Course 1)"
And I click on "Edit" "link" in the "Course meta link" "table_row"
And the field "Add to group" matches value "None"
And I am on the "Course 1" "enrolment methods" page
And I should not see "Course meta link (Course 1)"
@javascript
Scenario: Validation of groups for uploaded courses with meta enrolments
Given the following "groups" exist:
| name | course | idnumber |
| group1 | C2 | G1 |
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_meta_addtogroup_groupname.csv" file to "File" filemanager
And I click on "Preview" "button"
And I should see "You cannot specify groupname when addtogroup is set."
And I navigate to "Courses > Upload courses" in site administration
And I set the field "Upload mode" to "Create new courses, or update existing ones"
And I set the field "Update mode" to "Update with CSV data only"
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_meta_addtogroup.csv" file to "File" filemanager
And I click on "Preview" "button"
And I click on "Upload courses" "button"
And I should see "Courses created: 2"
And I should see "Courses errors: 0"
And I am on the "Course 3" "enrolment methods" page
And I should see "Course meta link (Course 1)"
And I click on "Edit" "link" in the "Course meta link" "table_row"
And the field "Add to group" matches value "Course 1 course"
And I am on the "Course 3" "groups" page
And I should see "Course 1 course"
And I am on the "Course 4" "enrolment methods" page
And I should see "Course meta link (Course 1)"
And I click on "Edit" "link" in the "Course meta link" "table_row"
And the field "Add to group" matches value "None"
And I am on the "Course 4" "groups" page
And I should not see "Course 1 course"
And I navigate to "Courses > Upload courses" in site administration
And I set the field "Upload mode" to "Create new courses, or update existing ones"
And I set the field "Update mode" to "Update with CSV data only"
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_meta_groups.csv" file to "File" filemanager
And I click on "Preview" "button"
And I should see "Error, invalid group notexist"
When I click on "Upload courses" "button"
And I should see "Error, invalid group notexist"
And I should see "Courses updated: 1"
And I should see "Courses errors: 1"
And I am on the "Course 2" "enrolment methods" page
Then I should see "Course meta link (Course 1)"
And I click on "Edit" "link" in the "Course meta link" "table_row"
And the field "Add to group" matches value "group1"
And I am on the "Course 2" "groups" page
And I should see "group1"
@@ -0,0 +1,122 @@
@tool @tool_uploadcourse @_file_upload
Feature: An admin can create courses with self enrolments using a CSV file
In order to create courses using a CSV file with self enrolment
As an admin
I need to be able to upload a CSV file and navigate through the import process
Background:
Given the following "categories" exist:
| name | category | idnumber |
| Cat 0 | 0 | CAT0 |
| Cat 1 | CAT0 | CAT1 |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | CAT1 |
And the following "groups" exist:
| name | course | idnumber | enrolmentkey |
| group1 | C1 | G1 | test |
And I log in as "admin"
And I navigate to "Courses > Upload courses" in site administration
And I set the field "Upload mode" to "Create new courses, or update existing ones"
And I set the field "Update mode" to "Update with CSV data only"
@javascript
Scenario: Validation of password for uploaded courses with self enrolments
# usepasswordpolicy is not set.
Given I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_self.csv" file to "File" filemanager
And I click on "Preview" "button"
And I click on "Upload courses" "button"
And I should see "Courses created: 2"
And I should see "Courses updated: 1"
And I should see "Courses errors: 0"
And I am on the "Course 1" "enrolment methods" page
And I click on "Edit" "link" in the "Self enrolment" "table_row"
And the field "Enrolment key" matches value "test"
And I press "Cancel"
And I click on "Delete" "link" in the "Self enrolment" "table_row"
And I press "Continue"
And I am on the "Course 2" "enrolment methods" page
And I click on "Edit" "link" in the "Self enrolment" "table_row"
And the field "Enrolment key" matches value ""
And I press "Cancel"
And I click on "Delete" "link" in the "Self enrolment" "table_row"
And I press "Continue"
And I am on the "Course 3" "enrolment methods" page
And I click on "Edit" "link" in the "Self enrolment" "table_row"
And the field "Enrolment key" matches value "Test123@"
And I press "Cancel"
And I click on "Delete" "link" in the "Self enrolment" "table_row"
And I press "Continue"
# Policy is used, but password not required so it will not be generated if omitted.
And the following config values are set as admin:
| config | value | plugin |
| usepasswordpolicy | 1 | enrol_self |
And I navigate to "Courses > Upload courses" in site administration
And I set the field "Upload mode" to "Create new courses, or update existing ones"
And I set the field "Update mode" to "Update with CSV data only"
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_self.csv" file to "File" filemanager
And I click on "Preview" "button"
And I should see "Passwords must be at least 8 characters long."
And I should see "Passwords must have at least 1 digit(s)."
And I should see "Passwords must have at least 1 upper case letter(s)."
And I should see "The password must have at least 1 special character(s) such as *, -, or #."
And I click on "Upload courses" "button"
And I should see "Courses created: 0"
And I should see "Courses updated: 2"
And I should see "Courses errors: 1"
And I am on the "Course 1" "enrolment methods" page
And "Self enrolment (Student)" "table_row" should not exist
And I am on the "Course 2" "enrolment methods" page
And I click on "Edit" "link" in the "Self enrolment" "table_row"
And the field "Enrolment key" matches value ""
And I press "Cancel"
And I click on "Delete" "link" in the "Self enrolment" "table_row"
And I press "Continue"
And I am on the "Course 3" "enrolment methods" page
And I click on "Edit" "link" in the "Self enrolment" "table_row"
And the field "Enrolment key" matches value "Test123@"
And I press "Cancel"
And I click on "Delete" "link" in the "Self enrolment" "table_row"
And I press "Continue"
# Policy is used and password not required so it will be generated if omitted.
And the following config values are set as admin:
| config | value | plugin |
| requirepassword | 1 | enrol_self |
And I navigate to "Courses > Upload courses" in site administration
And I set the field "Upload mode" to "Create new courses, or update existing ones"
And I set the field "Update mode" to "Update with CSV data only"
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_self.csv" file to "File" filemanager
And I click on "Preview" "button"
And I should see "Passwords must be at least 8 characters long."
And I should see "Passwords must have at least 1 digit(s)."
And I should see "Passwords must have at least 1 upper case letter(s)."
And I should see "The password must have at least 1 special character(s) such as *, -, or #."
And I click on "Upload courses" "button"
And I should see "Courses created: 0"
And I should see "Courses updated: 2"
And I should see "Courses errors: 1"
And I am on the "Course 1" "enrolment methods" page
And "Self enrolment (Student)" "table_row" should not exist
And I am on the "Course 2" "enrolment methods" page
And I click on "Edit" "link" in the "Self enrolment" "table_row"
And the field "Enrolment key" does not match value ""
And I am on the "Course 3" "enrolment methods" page
And I click on "Edit" "link" in the "Self enrolment" "table_row"
And the field "Enrolment key" matches value "Test123@"
# Use group enrolment keys is set to yes, so the password must be different from group enrolment key.
And I am on the "Course 1" "enrolment methods" page
And I add "Self enrolment" enrolment method in "Course 1" with:
| Enrolment key | Abcejfd12@ |
| Use group enrolment keys | Yes |
And the following config values are set as admin:
| config | value | plugin |
| usepasswordpolicy | 0 | enrol_self |
And I navigate to "Courses > Upload courses" in site administration
And I set the field "Upload mode" to "Create new courses, or update existing ones"
And I set the field "Update mode" to "Update with CSV data only"
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_self.csv" file to "File" filemanager
And I click on "Preview" "button"
And I should see "This enrolment key is already used as a group enrolment key."
@@ -0,0 +1,136 @@
@tool @tool_uploadcourse @_file_upload
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" exist:
| fullname | shortname | category |
| Some random name | C1 | 0 |
| Another course | CF1 | 0 |
And I log in as "admin"
And I navigate to "Courses > Upload courses" in site administration
@javascript
Scenario: Updating a course fullname
Given I upload "admin/tool/uploadcourse/tests/fixtures/courses.csv" file to "File" filemanager
And I set the field "Upload mode" to "Only update existing courses"
And I set the field "Update mode" to "Update with CSV data only"
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 am on site homepage
And I should see "Course 1"
And I should not see "Course 2"
And I should not see "Course 3"
@javascript
Scenario: Updating a course with custom fields
Given the following "custom field categories" exist:
| name | component | area | itemid |
| Other | core_course | course | 0 |
And the following "custom fields" exist:
| name | category | type | shortname | configdata |
| Field 1 | Other | checkbox | checkbox | |
| Field 2 | Other | date | date | |
| Field 3 | Other | select | select | {"options":"a\nb\nc"} |
| Field 4 | Other | text | text | |
| Field 5 | Other | textarea | textarea | |
When I upload "admin/tool/uploadcourse/tests/fixtures/courses_custom_fields.csv" file to "File" filemanager
And I set the following fields to these values:
| Upload mode | Only update existing courses |
| Update mode | Update with CSV data only |
And I click on "Preview" "button"
And I click on "Upload courses" "button"
Then I should see "Course updated"
And I should see "Courses updated: 1"
And I am on site homepage
And I should see "Course fields 1"
And I should see "Field 1: Yes"
And I should see "Field 2: Tuesday, 1 October 2019, 2:00"
And I should see "Field 3: b"
And I should see "Field 4: Hello"
And I should see "Field 5: Goodbye"
@javascript
Scenario: Unsupported enrol methods are not updated
Given the following config values are set as admin:
| enrol_plugins_enabled | manual,lti |
And the following "courses" exist:
| fullname | shortname | category |
| Course 2 | C2 | 0 |
And I am on the "C2" "enrolment methods" page
When I select "Publish as LTI tool" from the "Add method" singleselect
And the following fields match these values:
| LTI version | LTI Advantage |
And I set the following fields to these values:
| Custom instance name | Published course |
| Tool to be published | Course |
And I press "Add method"
And I should see "Published course"
And I navigate to "Courses > Upload courses" in site administration
And I set the field "Upload mode" to "Create new courses, or update existing ones"
And I set the field "Update mode" to "Update with CSV data only"
And I upload "admin/tool/uploadcourse/tests/fixtures/unsupported_enrol_method.csv" file to "File" filemanager
And I click on "Preview" "button"
And I click on "Upload courses" "button"
Then I should see "Enrolment method 'enrol_lti_plugin' is not supported in csv upload"
And I should see "Courses errors: 1"
And I am on the "C2" "enrolment methods" page
And I should see "manualtest"
And I should not see "Manual enrolments"
And I should see "Published course"
And I should not see "ltitest"
@javascript
Scenario: Manager can use upload course tool to update courses in course category
Given the following "users" exist:
| username | firstname | lastname | email |
| user1 | User | 1 | user1@example.com |
And the following "categories" exist:
| name | category | idnumber |
| Cat 1 | 0 | CAT1 |
| Cat 2 | 0 | CAT2 |
| Cat 3 | CAT1 | CAT3 |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C01 | CAT1 |
| Course 2 | C02 | CAT2 |
| Course 3 | C03 | CAT3 |
| Course 4 | C04 | CAT3 |
And the following "role assigns" exist:
| user | role | contextlevel | reference |
| user1 | manager | Category | CAT1 |
When I log in as "user1"
And I am on course index
And I follow "Cat 1"
And I navigate to "Upload courses" in current page administration
And I upload "admin/tool/uploadcourse/tests/fixtures/courses_manager2.csv" file to "File" filemanager
And I set the field "Upload mode" to "Only update existing courses"
And I set the field "Update mode" to "Update with CSV data only"
And I click on "Preview" "button"
# Course C01 is in "our" category but can not be moved to Cat 2 because current user can not manage it.
Then I should see "No permission to upload courses in category: Cat 2" in the "C01" "table_row"
# Course C02 can not be updated (no capability in "Cat 2" context).
And I should see "A course with this short name exists but you don't have permission to use the upload courses functionality to update it." in the "C02" "table_row"
# Course with short name "C05" does not exist.
And I should see "The course does not exist and creating course is not allowed" in the "C05" "table_row"
And I click on "Upload courses" "button"
And I should see "Course updated"
And I should see "Courses total: 5"
And I should see "Courses updated: 2"
And I should see "Courses errors: 3"
And I am on course index
And I follow "Cat 1"
# Course C04 was moved from Cat 3 to Cat 1.
And I should see "Course 4"
And I should see "Course 1"
And I follow "Cat 3"
And I should see "Course 3"
And I log out
File diff suppressed because it is too large Load Diff
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,2 @@
shortname,fullname,summary,category,customfield_checkbox,customfield_date,customfield_select,customfield_text,customfield_textarea
CF1,Course fields 1,Testing course fields,1,1,2019-10-01 14:00,b,Hello,Goodbye
1 shortname fullname summary category customfield_checkbox customfield_date customfield_select customfield_text customfield_textarea
2 CF1 Course fields 1 Testing course fields 1 1 2019-10-01 14:00 b Hello Goodbye
@@ -0,0 +1,6 @@
shortname,fullname,summary,idnumber,category_idnumber
C1,Course 1,Summary 1,ID1,CAT2
C2,Course 2,Summary 2,ID2,CAT2
C3,Course 3,Summary 3,ID3,
C4,Course 4,Summary 4,ID4,CAT1
C5,Course 5,Summary 5,ID5,CAT3
1 shortname fullname summary idnumber category_idnumber
2 C1 Course 1 Summary 1 ID1 CAT2
3 C2 Course 2 Summary 2 ID2 CAT2
4 C3 Course 3 Summary 3 ID3
5 C4 Course 4 Summary 4 ID4 CAT1
6 C5 Course 5 Summary 5 ID5 CAT3
@@ -0,0 +1,6 @@
shortname,category_idnumber
C01,CAT2
C02,CAT2
C03,
C04,CAT1
C05,CAT3
1 shortname category_idnumber
2 C01 CAT2
3 C02 CAT2
4 C03
5 C04 CAT1
6 C05 CAT3
@@ -0,0 +1,6 @@
shortname,fullname,category_idnumber,enrolment_1,enrolment_1_role,enrolment_1_cohortidnumber
C1,Course 1,CAT1,cohort,student,Not exist
C1,Course 1,CAT1,cohort,student,CV3
C2,Course 2,CAT2,cohort,student,CV4
C3,Course 3,CAT1,cohort,student,CV5
C4,Course 3,CAT1,cohort,student1,CV6
1 shortname fullname category_idnumber enrolment_1 enrolment_1_role enrolment_1_cohortidnumber
2 C1 Course 1 CAT1 cohort student Not exist
3 C1 Course 1 CAT1 cohort student CV3
4 C2 Course 2 CAT2 cohort student CV4
5 C3 Course 3 CAT1 cohort student CV5
6 C4 Course 3 CAT1 cohort student1 CV6
@@ -0,0 +1,3 @@
shortname,fullname,category_idnumber,enrolment_1,enrolment_1_role,enrolment_1_cohortidnumber,enrolment_1_addtogroup
C2,Course 2,CAT2,cohort,student,CV2,1
C3,Course 3,CAT1,cohort,student,CV1,0
1 shortname fullname category_idnumber enrolment_1 enrolment_1_role enrolment_1_cohortidnumber enrolment_1_addtogroup
2 C2 Course 2 CAT2 cohort student CV2 1
3 C3 Course 3 CAT1 cohort student CV1 0
@@ -0,0 +1,2 @@
shortname,fullname,category_idnumber,enrolment_1,enrolment_1_role,enrolment_1_cohortidnumber,enrolment_1_addtogroup,enrolment_1_groupname
C1,Course 1,CAT1,cohort,student,CV1,0,group1
1 shortname fullname category_idnumber enrolment_1 enrolment_1_role enrolment_1_cohortidnumber enrolment_1_addtogroup enrolment_1_groupname
2 C1 Course 1 CAT1 cohort student CV1 0 group1
@@ -0,0 +1,3 @@
shortname,fullname,category_idnumber,enrolment_1,enrolment_1_role,enrolment_1_cohortidnumber,enrolment_1_groupname
C1,Course 1,CAT1,cohort,student,CV1,notexist
C1,Course 1,CAT1,cohort,student,CV1,group1
1 shortname fullname category_idnumber enrolment_1 enrolment_1_role enrolment_1_cohortidnumber enrolment_1_groupname
2 C1 Course 1 CAT1 cohort student CV1 notexist
3 C1 Course 1 CAT1 cohort student CV1 group1
@@ -0,0 +1,2 @@
shortname,fullname,category_idnumber,enrolment_1
C1,Course 1,CAT1,cohort
1 shortname fullname category_idnumber enrolment_1
2 C1 Course 1 CAT1 cohort
@@ -0,0 +1,3 @@
shortname,fullname,category_idnumber,enrolment_1,enrolment_1_role,enrolment_1_cohortidnumber
C1,Course 1,CAT1,cohort,student,CV1
C1,Course 1,CAT1,cohort,teacher,CV4
1 shortname fullname category_idnumber enrolment_1 enrolment_1_role enrolment_1_cohortidnumber
2 C1 Course 1 CAT1 cohort student CV1
3 C1 Course 1 CAT1 cohort teacher CV4
@@ -0,0 +1,2 @@
shortname,category,enrolment_1,enrolment_1_delete
C1,1,guest,1
1 shortname category enrolment_1 enrolment_1_delete
2 C1 1 guest 1
@@ -0,0 +1,2 @@
shortname,category,enrolment_1,enrolment_1_disable
C1,1,guest,1
1 shortname category enrolment_1 enrolment_1_disable
2 C1 1 guest 1
@@ -0,0 +1,2 @@
shortname,category,enrolment_1,enrolment_1_disable
C1,1,guest,0
1 shortname category enrolment_1 enrolment_1_disable
2 C1 1 guest 0
@@ -0,0 +1,4 @@
shortname,fullname,category_idnumber,enrolment_1,enrolment_1_role,enrolment_1_password
C1,Course 1,CAT1,guest,student,test
C2,Course 2,CAT1,guest,student,
C3,Course 3,CAT1,guest,student,Test123@
1 shortname fullname category_idnumber enrolment_1 enrolment_1_role enrolment_1_password
2 C1 Course 1 CAT1 guest student test
3 C2 Course 2 CAT1 guest student
4 C3 Course 3 CAT1 guest student Test123@
@@ -0,0 +1,4 @@
shortname,fullname,category_idnumber,enrolment_1,enrolment_1_metacoursename
C3,Course 3,CAT1,meta,Not exist
C4,Course 4,CAT2,meta,C1
C1,Course 1,CAT1,meta,C1
1 shortname fullname category_idnumber enrolment_1 enrolment_1_metacoursename
2 C3 Course 3 CAT1 meta Not exist
3 C4 Course 4 CAT2 meta C1
4 C1 Course 1 CAT1 meta C1
@@ -0,0 +1,3 @@
shortname,fullname,category_idnumber,enrolment_1,enrolment_1_metacoursename,enrolment_1_addtogroup
C3,Course 3,CAT2,meta,C1,1
C4,Course 4,CAT1,meta,C1,0
1 shortname fullname category_idnumber enrolment_1 enrolment_1_metacoursename enrolment_1_addtogroup
2 C3 Course 3 CAT2 meta C1 1
3 C4 Course 4 CAT1 meta C1 0
@@ -0,0 +1,2 @@
shortname,fullname,category_idnumber,enrolment_1,enrolment_1_metacoursename,enrolment_1_addtogroup,enrolment_1_groupname
C2,Course 2,CAT1,meta,C1,0,group1
1 shortname fullname category_idnumber enrolment_1 enrolment_1_metacoursename enrolment_1_addtogroup enrolment_1_groupname
2 C2 Course 2 CAT1 meta C1 0 group1
@@ -0,0 +1,3 @@
shortname,fullname,category_idnumber,enrolment_1,enrolment_1_metacoursename,enrolment_1_groupname
C2,Course 2,CAT1,meta,C1,notexist
C2,Course 2,CAT1,meta,C1,group1
1 shortname fullname category_idnumber enrolment_1 enrolment_1_metacoursename enrolment_1_groupname
2 C2 Course 2 CAT1 meta C1 notexist
3 C2 Course 2 CAT1 meta C1 group1
@@ -0,0 +1,2 @@
shortname,fullname,category_idnumber,enrolment_1,enrolment_1_role,enrolment_2,enrolment_2_role,enrolment_2_cohortidnumber,enrolment_3,enrolment_4,enrolment_4_role,enrolment_5,enrolment_5_metacoursename
C2,Course 2,CAT1,self,student,cohort,student,Cohort1,guest,manual,student,meta,C1
1 shortname fullname category_idnumber enrolment_1 enrolment_1_role enrolment_2 enrolment_2_role enrolment_2_cohortidnumber enrolment_3 enrolment_4 enrolment_4_role enrolment_5 enrolment_5_metacoursename
2 C2 Course 2 CAT1 self student cohort student Cohort1 guest manual student meta C1
@@ -0,0 +1,2 @@
shortname,fullname,category_idnumber,enrolment_1,enrolment_1_delete,enrolment_2,enrolment_2_role,enrolment_2_cohortidnumber,enrolment_2_delete,enrolment_3,enrolment_3_delete,enrolment_4,enrolment_4_delete,enrolment_5,enrolment_5_metacoursename,enrolment_5_delete
C2,Course 2,CAT1,self,1,cohort,student,Cohort1,1,guest,1,manual,1,meta,C1,1
1 shortname fullname category_idnumber enrolment_1 enrolment_1_delete enrolment_2 enrolment_2_role enrolment_2_cohortidnumber enrolment_2_delete enrolment_3 enrolment_3_delete enrolment_4 enrolment_4_delete enrolment_5 enrolment_5_metacoursename enrolment_5_delete
2 C2 Course 2 CAT1 self 1 cohort student Cohort1 1 guest 1 manual 1 meta C1 1
@@ -0,0 +1,4 @@
shortname,fullname,category,enrolment_1,enrolment_1_role
CX,coursex,1,manual,notexist
CY,coursey,1,manual,notallowed
CZ,coursez,1,manual,student
1 shortname fullname category enrolment_1 enrolment_1_role
2 CX coursex 1 manual notexist
3 CY coursey 1 manual notallowed
4 CZ coursez 1 manual student
@@ -0,0 +1,4 @@
shortname,fullname,category_idnumber,enrolment_1,enrolment_1_role,enrolment_1_password
C1,Course 1,CAT1,self,student,test
C2,Course 2,CAT1,self,student,
C3,Course 3,CAT1,self,student,Test123@
1 shortname fullname category_idnumber enrolment_1 enrolment_1_role enrolment_1_password
2 C1 Course 1 CAT1 self student test
3 C2 Course 2 CAT1 self student
4 C3 Course 3 CAT1 self student Test123@
@@ -0,0 +1,3 @@
shortname,fullname,category_idnumber,enrolment_1,enrolment_1_role,enrolment_1_name
C2,Course 2,0,manual,student,manualtest
C2,Course 2,0,lti,student,ltitest
1 shortname fullname category_idnumber enrolment_1 enrolment_1_role enrolment_1_name
2 C2 Course 2 0 manual student manualtest
3 C2 Course 2 0 lti student ltitest
@@ -0,0 +1,497 @@
<?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/>.
namespace tool_uploadcourse;
use tool_uploadcourse_helper;
/**
* 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 helper_test extends \advanced_testcase {
public function test_generate_shortname(): void {
$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(): void {
$result = tool_uploadcourse_helper::get_course_formats();
$this->assertSame(array_keys(\core_component::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(): void {
$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(): void {
$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(): void {
global $CFG;
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
$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();
$oldcfg = isset($CFG->keeptempdirectoriesonbackup) ? $CFG->keeptempdirectoriesonbackup : false;
$CFG->keeptempdirectoriesonbackup = true;
// 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);
// Trying again without caching. $CFG->keeptempdirectoriesonbackup is required for caching.
$CFG->keeptempdirectoriesonbackup = false;
// Checking restore dir.
$dir = tool_uploadcourse_helper::get_restore_content_dir($c1backupfile, null);
$dir2 = tool_uploadcourse_helper::get_restore_content_dir($c1backupfile, null);
$this->assertNotEquals($dir, $dir2);
// Checking with a shortname.
$dir = tool_uploadcourse_helper::get_restore_content_dir(null, $c1->shortname);
$dir2 = tool_uploadcourse_helper::get_restore_content_dir(null, $c1->shortname);
$this->assertNotEquals($dir, $dir2);
// 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);
$dir2 = tool_uploadcourse_helper::get_restore_content_dir(null, 'DoesNotExist', $errors);
$this->assertEquals($dir, $dir2);
$CFG->keeptempdirectoriesonbackup = $oldcfg;
}
public function test_get_role_ids(): void {
$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(): void {
$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);
}
/**
* Test custom field data processing
*/
public function test_get_custom_course_field_data(): void {
global $DB;
$this->resetAfterTest();
// Create all the fields!
$category = $this->get_customfield_generator()->create_category();
$checkboxfield = $this->create_custom_field($category, 'checkbox', 'mycheckbox');
$datefield = $this->create_custom_field($category, 'date', 'mydate');
$selectfield = $this->create_custom_field($category, 'select', 'myselect', ['options' => "Red\nGreen\nBlue"]);
$textfield = $this->create_custom_field($category, 'text', 'mytext', ['locked' => 1]);
$textareafield = $this->create_custom_field($category, 'textarea', 'mytextarea');
$fields = tool_uploadcourse_helper::get_custom_course_fields();
$this->assertCount(5, $fields);
$this->assertArrayHasKey($checkboxfield->get('shortname'), $fields);
$this->assertInstanceOf(\customfield_checkbox\field_controller::class, $fields[$checkboxfield->get('shortname')]);
$this->assertArrayHasKey($datefield->get('shortname'), $fields);
$this->assertInstanceOf(\customfield_date\field_controller::class, $fields[$datefield->get('shortname')]);
$this->assertArrayHasKey($selectfield->get('shortname'), $fields);
$this->assertInstanceOf(\customfield_select\field_controller::class, $fields[$selectfield->get('shortname')]);
$this->assertArrayHasKey($textfield->get('shortname'), $fields);
$this->assertInstanceOf(\customfield_text\field_controller::class, $fields[$textfield->get('shortname')]);
$this->assertArrayHasKey($textareafield->get('shortname'), $fields);
$this->assertInstanceOf(\customfield_textarea\field_controller::class, $fields[$textareafield->get('shortname')]);
$data = [
'customfield_mycheckbox' => '1',
'customfield_mydate' => '2019-10-01',
'customfield_myselect' => 'Green',
'customfield_mytext' => 'Hello',
'customfield_myunknownfield' => 'Goodbye',
];
$expected = [
'customfield_mycheckbox' => '1',
'customfield_mydate' => strtotime('2019-10-01'),
'customfield_myselect' => 2,
'customfield_mytext' => 'Hello',
];
$course = $this->getDataGenerator()->create_course();
$user = $this->getDataGenerator()->create_and_enrol($course, 'manager');
$this->setUser($user);
$context = \context_course::instance($course->id);
$this->assertEquals($expected, tool_uploadcourse_helper::get_custom_course_field_data($data, [], $context));
// Now add our custom textarea field (separately because the value of it's 'itemid' element is unknown).
$data['customfield_mytextarea'] = 'Something';
$fields = tool_uploadcourse_helper::get_custom_course_field_data($data, [], $context);
$this->assertArrayHasKey('customfield_mytextarea_editor', $fields);
$this->assertArrayHasKey('text', $fields['customfield_mytextarea_editor']);
$this->assertEquals('Something', $fields['customfield_mytextarea_editor']['text']);
// Now prohibit the capability to change locked fields for the manager role.
$managerrole = $DB->get_record('role', ['shortname' => 'manager']);
role_change_permission($managerrole->id, $context, 'moodle/course:changelockedcustomfields', CAP_PROHIBIT);
// The locked 'mytext' custom field should not be returned.
$fields = tool_uploadcourse_helper::get_custom_course_field_data($data, [], $context);
$this->assertCount(4, $fields);
$this->assertArrayNotHasKey('customfield_mytext', $fields);
}
public function test_increment_idnumber(): void {
$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(): void {
$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(): void {
$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(): void {
$this->resetAfterTest(true);
$c1 = $this->getDataGenerator()->create_category(array('idnumber' => 'C1'));
$c2 = $this->getDataGenerator()->create_category(array('idnumber' => 'C2'));
$c3 = $this->getDataGenerator()->create_category(['idnumber' => 'C3', 'visible' => false]);
// 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('C3'));
$this->assertEmpty(tool_uploadcourse_helper::resolve_category_by_idnumber('C3'));
$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(): void {
$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->assertEmpty(tool_uploadcourse_helper::resolve_category_by_path($path));
$this->assertEmpty(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));
}
/**
* Get custom field plugin generator
*
* @return core_customfield_generator
*/
protected function get_customfield_generator(): \core_customfield_generator {
return $this->getDataGenerator()->get_plugin_generator('core_customfield');
}
/**
* Helper method to create custom course field
*
* @param \core_customfield\category_controller $category
* @param string $type
* @param string $shortname
* @param array $configdata
* @return \core_customfield\field_controller
*/
protected function create_custom_field(\core_customfield\category_controller $category, string $type, string $shortname,
array $configdata = []): \core_customfield\field_controller {
return $this->get_customfield_generator()->create_field([
'categoryid' => $category->get('id'),
'type' => $type,
'shortname' => $shortname,
'configdata' => $configdata,
]);
}
}
@@ -0,0 +1,217 @@
<?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/>.
namespace tool_uploadcourse;
use tool_uploadcourse_processor;
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 processor_test extends \advanced_testcase {
public function test_basic(): void {
global $DB;
$this->resetAfterTest(true);
$this->setAdminUser();
$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(): void {
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);
}
public function test_restore_restore_file(): void {
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);
}
public function test_shortname_template(): void {
global $DB;
$this->resetAfterTest(true);
$this->setAdminUser();
$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(): void {
$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->expectException(\moodle_exception::class);
$p = new tool_uploadcourse_processor($cir, $options, array());
}
public function test_not_enough_columns(): void {
$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->expectException(\moodle_exception::class);
$p = new tool_uploadcourse_processor($cir, $options, array());
}
public function test_preview(): void {
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 = 2024042200; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2024041600; // Requires this Moodle version.
$plugin->component = 'tool_uploadcourse'; // Full name of the plugin (used for diagnostics).