Merge branch 'MDL-43056-main' of https://github.com/davewoloszyn/moodle
This commit is contained in:
@@ -22,6 +22,8 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
use tool_uploadcourse\permissions;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
|
||||
require_once($CFG->dirroot . '/course/lib.php');
|
||||
@@ -448,6 +450,11 @@ class tool_uploadcourse_course {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($error = permissions::check_permission_to_delete($this->shortname)) {
|
||||
$this->error('coursedeletionpermission', $error);
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->do = self::DO_DELETE;
|
||||
return true;
|
||||
}
|
||||
@@ -680,9 +687,20 @@ class tool_uploadcourse_course {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($error = permissions::check_permission_to_update($coursedata)) {
|
||||
$this->error('cannotupdatepermission', $error);
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->do = self::DO_UPDATE;
|
||||
} else {
|
||||
$coursedata = $this->get_final_create_data($coursedata);
|
||||
|
||||
if ($error = permissions::check_permission_to_create($coursedata)) {
|
||||
$this->error('courseuploadnotallowed', $error);
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->do = self::DO_CREATE;
|
||||
}
|
||||
|
||||
@@ -799,6 +817,7 @@ class tool_uploadcourse_course {
|
||||
$this->data = $coursedata;
|
||||
|
||||
// Get enrolment data. Where the course already exists, we can also perform validation.
|
||||
// Some data is impossible to validate without the existing course, we will do it again during actual upload.
|
||||
$this->enrolmentdata = tool_uploadcourse_helper::get_enrolment_data($this->rawdata);
|
||||
$courseid = $coursedata['id'] ?? 0;
|
||||
$errors = $this->validate_enrolment_data($courseid, $this->enrolmentdata);
|
||||
@@ -823,6 +842,11 @@ class tool_uploadcourse_course {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->restoredata && ($error = permissions::check_permission_to_restore($this->do, $this->data))) {
|
||||
$this->error('courserestorepermission', $error);
|
||||
return false;
|
||||
}
|
||||
|
||||
// We can only reset courses when allowed and we are updating the course.
|
||||
if ($this->importoptions['reset'] || $this->options['reset']) {
|
||||
if ($this->do !== self::DO_UPDATE) {
|
||||
@@ -833,6 +857,11 @@ class tool_uploadcourse_course {
|
||||
$this->error('courseresetnotallowed', new lang_string('courseresetnotallowed', 'tool_uploadcourse'));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($error = permissions::check_permission_to_reset($this->data)) {
|
||||
$this->error('courseresetpermission', $error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -889,7 +918,7 @@ class tool_uploadcourse_course {
|
||||
$rc->execute_plan();
|
||||
$this->status('courserestored', new lang_string('courserestored', 'tool_uploadcourse'));
|
||||
} else {
|
||||
$this->error('errorwhilerestoringcourse', new lang_string('errorwhilerestoringthecourse', 'tool_uploadcourse'));
|
||||
$this->error('errorwhilerestoringcourse', new lang_string('errorwhilerestoringcourse', 'tool_uploadcourse'));
|
||||
}
|
||||
$rc->destroy();
|
||||
}
|
||||
@@ -913,7 +942,7 @@ class tool_uploadcourse_course {
|
||||
/**
|
||||
* Validate passed enrolment data against an existing course
|
||||
*
|
||||
* @param int $courseid
|
||||
* @param int $courseid id of the course where enrolment methods are created/updated or 0 if it is a new course
|
||||
* @param array[] $enrolmentdata
|
||||
* @return lang_string[] Errors keyed on error code
|
||||
*/
|
||||
@@ -1050,8 +1079,13 @@ class tool_uploadcourse_course {
|
||||
// Create/update enrolment.
|
||||
$plugin = $enrolmentplugins[$enrolmethod];
|
||||
|
||||
if ($plugin->is_csv_upload_supported()) {
|
||||
// In case we could not properly validate enrolment data before the course existed
|
||||
// let's repeat it again here.
|
||||
$errors = $plugin->validate_enrol_plugin_data($method, $course->id);
|
||||
|
||||
if (!$errors) {
|
||||
$status = ($todisable) ? ENROL_INSTANCE_DISABLED : ENROL_INSTANCE_ENABLED;
|
||||
$method += ['status' => $status, 'courseid' => $course->id, 'id' => $instance->id ?? null];
|
||||
$method = $plugin->fill_enrol_custom_fields($method, $course->id);
|
||||
|
||||
// Create a new instance if necessary.
|
||||
@@ -1150,9 +1184,9 @@ class tool_uploadcourse_course {
|
||||
|
||||
$plugin->update_instance($instance, $modifiedinstance);
|
||||
} else {
|
||||
$this->error('errorunsupportedmethod',
|
||||
new lang_string('errorunsupportedmethod', 'tool_uploadcourse',
|
||||
$enrolmethod));
|
||||
foreach ($errors as $key => $message) {
|
||||
$this->error($key, $message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -540,6 +540,11 @@ class tool_uploadcourse_helper {
|
||||
$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;
|
||||
@@ -578,6 +583,11 @@ class tool_uploadcourse_helper {
|
||||
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 {
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -174,7 +174,7 @@ class tool_uploadcourse_processor {
|
||||
/**
|
||||
* Execute the process.
|
||||
*
|
||||
* @param object $tracker the output tracker to use.
|
||||
* @param tool_uploadcourse_tracker $tracker the output tracker to use.
|
||||
* @return void
|
||||
*/
|
||||
public function execute($tracker = null) {
|
||||
@@ -324,7 +324,7 @@ class tool_uploadcourse_processor {
|
||||
* This only returns passed data, along with the errors.
|
||||
*
|
||||
* @param integer $rows number of rows to preview.
|
||||
* @param object $tracker the output tracker to use.
|
||||
* @param tool_uploadcourse_tracker $tracker the output tracker to use.
|
||||
* @return array of preview data.
|
||||
*/
|
||||
public function preview($rows = 10, $tracker = null) {
|
||||
|
||||
@@ -74,6 +74,9 @@ class tool_uploadcourse_step1_form extends tool_uploadcourse_base_form {
|
||||
$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'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ class tool_uploadcourse_step2_form extends tool_uploadcourse_base_form {
|
||||
$mform->addElement('header', 'defaultheader', get_string('defaultvalues', 'tool_uploadcourse'));
|
||||
$mform->setExpanded('defaultheader', true);
|
||||
|
||||
$displaylist = core_course_category::make_categories_list('moodle/course:create');
|
||||
$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');
|
||||
@@ -223,6 +223,9 @@ class tool_uploadcourse_step2_form extends tool_uploadcourse_base_form {
|
||||
$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.
|
||||
|
||||
@@ -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,
|
||||
],
|
||||
],
|
||||
];
|
||||
@@ -26,15 +26,31 @@ require(__DIR__ . '/../../../config.php');
|
||||
require_once($CFG->libdir . '/adminlib.php');
|
||||
require_once($CFG->libdir . '/csvlib.class.php');
|
||||
|
||||
admin_externalpage_setup('tooluploadcourse');
|
||||
|
||||
$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');
|
||||
@@ -58,7 +74,8 @@ if (empty($importid)) {
|
||||
}
|
||||
|
||||
// Data to set in the form.
|
||||
$data = array('importid' => $importid, 'previewrows' => $previewrows);
|
||||
$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) {
|
||||
@@ -117,7 +134,7 @@ if ($form2data = $mform2->is_cancelled()) {
|
||||
// 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, array());
|
||||
$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));
|
||||
|
||||
@@ -67,6 +67,8 @@ $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'] = "Course with this shortname exists and you don't have permission to use upload course tool 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';
|
||||
@@ -128,6 +130,7 @@ $string['updatemodedoessettonothing'] = 'Update mode does not allow anything to
|
||||
$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:
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,10 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
if ($hassiteconfig) {
|
||||
$ADMIN->add('courses', new admin_externalpage('tooluploadcourse',
|
||||
get_string('uploadcourses', 'tool_uploadcourse'), "$CFG->wwwroot/$CFG->admin/tool/uploadcourse/index.php"));
|
||||
}
|
||||
$ADMIN->add('courses',
|
||||
new admin_externalpage('tooluploadcourse',
|
||||
get_string('uploadcourses', 'tool_uploadcourse'),
|
||||
"$CFG->wwwroot/$CFG->admin/tool/uploadcourse/index.php",
|
||||
'tool/uploadcourse:use'
|
||||
)
|
||||
);
|
||||
|
||||
@@ -145,3 +145,39 @@ Feature: An admin can create courses using a CSV file
|
||||
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
|
||||
|
||||
@@ -87,3 +87,50 @@ Feature: An admin can update courses using a CSV file
|
||||
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 "Course with this shortname exists and you don't have permission to use upload course tool 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
|
||||
|
||||
@@ -22,14 +22,54 @@ use tool_uploadcourse_course;
|
||||
/**
|
||||
* Course test case.
|
||||
*
|
||||
* @covers \tool_uploadcourse_course
|
||||
* @package tool_uploadcourse
|
||||
* @copyright 2013 Frédéric Massart
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or late
|
||||
*/
|
||||
class course_test extends \advanced_testcase {
|
||||
|
||||
public function test_proceed_without_prepare() {
|
||||
/** @var \testing_data_generator $datagenerator */
|
||||
protected $datagenerator;
|
||||
/** @var \stdClass $user */
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* Initialise defaults for each testcase.
|
||||
*
|
||||
* @param int $roleid
|
||||
* @throws \coding_exception
|
||||
*/
|
||||
protected function initialise_test(int $roleid = 1): void {
|
||||
// Reset the database after test.
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
// Get a new data generator.
|
||||
$this->datagenerator = $this->getDataGenerator();
|
||||
|
||||
// Create a user.
|
||||
$this->prepare_user($roleid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create random user and assign default role Manager (roleid = 1).
|
||||
*
|
||||
* @param int $roleid
|
||||
* @throws \coding_exception
|
||||
*/
|
||||
protected function prepare_user(int $roleid): void {
|
||||
// Generate a random user.
|
||||
$user = $this->datagenerator->create_user();
|
||||
|
||||
// Log the user in (set the $USER global variable).
|
||||
$this->setUser($user);
|
||||
|
||||
// Assign a role to the current user.
|
||||
$this->datagenerator->role_assign($roleid, $user->id, false);
|
||||
}
|
||||
|
||||
public function test_proceed_without_prepare(): void {
|
||||
$this->initialise_test();
|
||||
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
|
||||
$updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
|
||||
$data = array();
|
||||
@@ -39,7 +79,7 @@ class course_test extends \advanced_testcase {
|
||||
}
|
||||
|
||||
public function test_proceed_when_prepare_failed() {
|
||||
$this->resetAfterTest(true);
|
||||
$this->initialise_test();
|
||||
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
|
||||
$updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
|
||||
$data = array();
|
||||
@@ -50,7 +90,7 @@ class course_test extends \advanced_testcase {
|
||||
}
|
||||
|
||||
public function test_proceed_when_already_started() {
|
||||
$this->resetAfterTest(true);
|
||||
$this->initialise_test();
|
||||
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
|
||||
$updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
|
||||
$data = array('shortname' => 'test', 'fullname' => 'New course', 'summary' => 'New', 'category' => 1);
|
||||
@@ -62,7 +102,7 @@ class course_test extends \advanced_testcase {
|
||||
}
|
||||
|
||||
public function test_invalid_shortname() {
|
||||
$this->resetAfterTest(true);
|
||||
$this->initialise_test();
|
||||
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
|
||||
$updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
|
||||
$data = array('shortname' => '<invalid>', 'fullname' => 'New course', 'summary' => 'New', 'category' => 1);
|
||||
@@ -88,7 +128,7 @@ class course_test extends \advanced_testcase {
|
||||
}
|
||||
|
||||
public function test_invalid_fullname_too_long() {
|
||||
$this->resetAfterTest();
|
||||
$this->initialise_test();
|
||||
|
||||
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
|
||||
$updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
|
||||
@@ -103,7 +143,7 @@ class course_test extends \advanced_testcase {
|
||||
}
|
||||
|
||||
public function test_invalid_visibility() {
|
||||
$this->resetAfterTest(true);
|
||||
$this->initialise_test();
|
||||
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
|
||||
$updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
|
||||
$data = array('shortname' => 'test', 'fullname' => 'New course', 'summary' => 'New', 'category' => 1, 'visible' => 2);
|
||||
@@ -194,9 +234,48 @@ class course_test extends \advanced_testcase {
|
||||
$this->assertArrayHasKey('invaliddownloadcontent', $upload->get_errors());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a role's capability to use the upload course tool.
|
||||
*
|
||||
* @covers \permissions::check_permission_to_use_uploadcourse_tool
|
||||
*/
|
||||
public function test_invalid_role(): void {
|
||||
global $DB;
|
||||
|
||||
$rolesallowed = ['manager'];
|
||||
$roles = get_all_roles();
|
||||
|
||||
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
|
||||
$updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
|
||||
|
||||
foreach ($roles as $role) {
|
||||
$this->initialise_test($role->id);
|
||||
|
||||
$data = ['shortname' => 'newcourse', 'fullname' => 'New course', 'summary' => 'New', 'category' => 1];
|
||||
$co = new tool_uploadcourse_course($mode, $updatemode, $data);
|
||||
|
||||
if (in_array($role->shortname, $rolesallowed)) {
|
||||
$this->assertTrue($co->prepare());
|
||||
$co->proceed();
|
||||
$courseid = $DB->get_field('course', 'id', ['shortname' => 'newcourse'], MUST_EXIST);
|
||||
$this->assertEquals(0, course_get_format($courseid)->get_course()->coursedisplay);
|
||||
|
||||
// Delete course for next assertion.
|
||||
$importoptions = ['candelete' => true];
|
||||
$data = ['shortname' => 'newcourse', 'delete' => 1];
|
||||
$co = new tool_uploadcourse_course($mode, $updatemode, $data, [], $importoptions);
|
||||
$this->assertTrue($co->prepare());
|
||||
$co->proceed();
|
||||
} else {
|
||||
$this->assertFalse($co->prepare());
|
||||
$this->assertArrayHasKey('courseuploadnotallowed', $co->get_errors());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function test_create() {
|
||||
global $DB;
|
||||
$this->resetAfterTest(true);
|
||||
$this->initialise_test();
|
||||
|
||||
// Existing course.
|
||||
$c1 = $this->getDataGenerator()->create_course(array('shortname' => 'c1', 'summary' => 'Yay!'));
|
||||
@@ -245,7 +324,7 @@ class course_test extends \advanced_testcase {
|
||||
|
||||
public function test_create_with_sections() {
|
||||
global $DB;
|
||||
$this->resetAfterTest(true);
|
||||
$this->initialise_test();
|
||||
$updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
|
||||
$defaultnumsections = get_config('moodlecourse', 'numsections');
|
||||
|
||||
@@ -275,7 +354,7 @@ class course_test extends \advanced_testcase {
|
||||
|
||||
public function test_delete() {
|
||||
global $DB;
|
||||
$this->resetAfterTest(true);
|
||||
$this->initialise_test();
|
||||
|
||||
$c1 = $this->getDataGenerator()->create_course();
|
||||
$c2 = $this->getDataGenerator()->create_course();
|
||||
@@ -320,7 +399,7 @@ class course_test extends \advanced_testcase {
|
||||
|
||||
public function test_update() {
|
||||
global $DB;
|
||||
$this->resetAfterTest(true);
|
||||
$this->initialise_test();
|
||||
|
||||
$c1 = $this->getDataGenerator()->create_course(array('shortname' => 'c1'));
|
||||
|
||||
@@ -398,8 +477,7 @@ class course_test extends \advanced_testcase {
|
||||
public function test_data_saved() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
$this->setAdminUser(); // To avoid warnings related to 'moodle/course:setforcedlanguage' capability check.
|
||||
$this->initialise_test();
|
||||
|
||||
set_config('downloadcoursecontentallowed', 1);
|
||||
|
||||
@@ -617,8 +695,7 @@ class course_test extends \advanced_testcase {
|
||||
public function test_default_data_saved() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
$this->setAdminUser();
|
||||
$this->initialise_test();
|
||||
|
||||
set_config('downloadcoursecontentallowed', 1);
|
||||
|
||||
@@ -742,7 +819,7 @@ class course_test extends \advanced_testcase {
|
||||
|
||||
public function test_rename() {
|
||||
global $DB;
|
||||
$this->resetAfterTest(true);
|
||||
$this->initialise_test();
|
||||
|
||||
$c1 = $this->getDataGenerator()->create_course(array('shortname' => 'c1'));
|
||||
$c2 = $this->getDataGenerator()->create_course(array('shortname' => 'c2'));
|
||||
@@ -837,7 +914,7 @@ class course_test extends \advanced_testcase {
|
||||
|
||||
public function test_restore_course() {
|
||||
global $DB;
|
||||
$this->resetAfterTest(true);
|
||||
$this->initialise_test();
|
||||
$this->setAdminUser();
|
||||
|
||||
$c1 = $this->getDataGenerator()->create_course();
|
||||
@@ -881,7 +958,7 @@ class course_test extends \advanced_testcase {
|
||||
|
||||
public function test_restore_file() {
|
||||
global $DB;
|
||||
$this->resetAfterTest(true);
|
||||
$this->initialise_test();
|
||||
$this->setAdminUser();
|
||||
|
||||
$c1 = $this->getDataGenerator()->create_course();
|
||||
@@ -933,7 +1010,7 @@ class course_test extends \advanced_testcase {
|
||||
*/
|
||||
public function test_restore_file_settings() {
|
||||
global $DB;
|
||||
$this->resetAfterTest(true);
|
||||
$this->initialise_test();
|
||||
$this->setAdminUser();
|
||||
|
||||
// Set admin config setting so that activities are not restored by default.
|
||||
@@ -956,7 +1033,7 @@ class course_test extends \advanced_testcase {
|
||||
}
|
||||
|
||||
public function test_restore_invalid_file() {
|
||||
$this->resetAfterTest();
|
||||
$this->initialise_test();
|
||||
|
||||
// Restore from a non-existing file should not be allowed.
|
||||
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
|
||||
@@ -983,7 +1060,7 @@ class course_test extends \advanced_testcase {
|
||||
}
|
||||
|
||||
public function test_restore_invalid_course() {
|
||||
$this->resetAfterTest();
|
||||
$this->initialise_test();
|
||||
|
||||
// Restore from an invalid file should not be allowed.
|
||||
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
|
||||
@@ -1000,7 +1077,7 @@ class course_test extends \advanced_testcase {
|
||||
*/
|
||||
public function test_reset() {
|
||||
global $DB;
|
||||
$this->resetAfterTest(true);
|
||||
$this->initialise_test();
|
||||
|
||||
$c1 = $this->getDataGenerator()->create_course();
|
||||
$c1ctx = \context_course::instance($c1->id);
|
||||
@@ -1092,7 +1169,7 @@ class course_test extends \advanced_testcase {
|
||||
|
||||
public function test_create_bad_category() {
|
||||
global $DB;
|
||||
$this->resetAfterTest(true);
|
||||
$this->initialise_test();
|
||||
|
||||
// Ensure fails when category cannot be resolved upon creation.
|
||||
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
|
||||
@@ -1146,7 +1223,7 @@ class course_test extends \advanced_testcase {
|
||||
}
|
||||
|
||||
public function test_enrolment_data() {
|
||||
$this->resetAfterTest(true);
|
||||
$this->initialise_test();
|
||||
|
||||
// We need to set the current user as one with the capability to edit manual enrolment instances in the new course.
|
||||
$this->setAdminUser();
|
||||
@@ -1425,7 +1502,7 @@ class course_test extends \advanced_testcase {
|
||||
}
|
||||
|
||||
public function test_idnumber_problems() {
|
||||
$this->resetAfterTest(true);
|
||||
$this->initialise_test();
|
||||
|
||||
$c1 = $this->getDataGenerator()->create_course(array('shortname' => 'sntaken', 'idnumber' => 'taken'));
|
||||
$c2 = $this->getDataGenerator()->create_course();
|
||||
@@ -1476,7 +1553,7 @@ class course_test extends \advanced_testcase {
|
||||
}
|
||||
|
||||
public function test_generate_shortname() {
|
||||
$this->resetAfterTest(true);
|
||||
$this->initialise_test();
|
||||
|
||||
$c1 = $this->getDataGenerator()->create_course(array('shortname' => 'taken'));
|
||||
|
||||
@@ -1529,7 +1606,7 @@ class course_test extends \advanced_testcase {
|
||||
|
||||
public function test_mess_with_frontpage() {
|
||||
global $SITE;
|
||||
$this->resetAfterTest(true);
|
||||
$this->initialise_test();
|
||||
|
||||
// Updating the front page.
|
||||
$mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
|
||||
|
||||
@@ -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
|
||||
|
@@ -0,0 +1,6 @@
|
||||
shortname,category_idnumber
|
||||
C01,CAT2
|
||||
C02,CAT2
|
||||
C03,
|
||||
C04,CAT1
|
||||
C05,CAT3
|
||||
|
@@ -381,12 +381,15 @@ class helper_test extends \advanced_testcase {
|
||||
|
||||
$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'));
|
||||
}
|
||||
@@ -436,8 +439,8 @@ class helper_test extends \advanced_testcase {
|
||||
|
||||
// Hidden parent.
|
||||
$path = array('Cat 2', 'Cat 2.1', 'Cat 2.1.2');
|
||||
$this->assertEquals($cat2_1_2->id, tool_uploadcourse_helper::resolve_category_by_path($path));
|
||||
$this->assertEquals($cat2_1_2->id, tool_uploadcourse_helper::resolve_category_by_path($path));
|
||||
$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');
|
||||
|
||||
@@ -35,6 +35,7 @@ class processor_test extends \advanced_testcase {
|
||||
public function test_basic() {
|
||||
global $DB;
|
||||
$this->resetAfterTest(true);
|
||||
$this->setAdminUser();
|
||||
|
||||
$content = array(
|
||||
"shortname,fullname,summary",
|
||||
@@ -134,6 +135,7 @@ class processor_test extends \advanced_testcase {
|
||||
public function test_shortname_template() {
|
||||
global $DB;
|
||||
$this->resetAfterTest(true);
|
||||
$this->setAdminUser();
|
||||
|
||||
$content = array(
|
||||
"shortname,fullname,summary,idnumber",
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2023100900; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2023112800; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2023100400; // Requires this Moodle version.
|
||||
$plugin->component = 'tool_uploadcourse'; // Full name of the plugin (used for diagnostics).
|
||||
|
||||
@@ -620,15 +620,19 @@ class enrol_cohort_plugin extends enrol_plugin {
|
||||
* @return lang_string|null Error
|
||||
*/
|
||||
public function validate_plugin_data_context(array $enrolmentdata, ?int $courseid = null) : ?lang_string {
|
||||
$error = null;
|
||||
if (isset($enrolmentdata['customint1'])) {
|
||||
$cohortid = $enrolmentdata['customint1'];
|
||||
$coursecontext = \context_course::instance($courseid);
|
||||
if (!cohort_get_cohort($cohortid, $coursecontext)) {
|
||||
$error = new lang_string('contextcohortnotallowed', 'cohort', $enrolmentdata['cohortidnumber']);
|
||||
return new lang_string('contextcohortnotallowed', 'cohort', $enrolmentdata['cohortidnumber']);
|
||||
}
|
||||
}
|
||||
return $error;
|
||||
$enrolmentdata += [
|
||||
'customint1' => null,
|
||||
'customint2' => null,
|
||||
'roleid' => 0,
|
||||
];
|
||||
return parent::validate_plugin_data_context($enrolmentdata, $courseid);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -246,7 +246,11 @@ class lib_test extends \advanced_testcase {
|
||||
$enrolmentdata = [
|
||||
'customint1' => $cohort1->id,
|
||||
'cohortidnumber' => $cohort1->idnumber,
|
||||
'courseid' => $course->id,
|
||||
'id' => null,
|
||||
'status' => ENROL_INSTANCE_ENABLED,
|
||||
];
|
||||
$enrolmentdata = $cohortplugin->fill_enrol_custom_fields($enrolmentdata, $course->id);
|
||||
$error = $cohortplugin->validate_plugin_data_context($enrolmentdata, $course->id);
|
||||
$this->assertNull($error);
|
||||
}
|
||||
@@ -313,6 +317,7 @@ class lib_test extends \advanced_testcase {
|
||||
*/
|
||||
public function test_validate_enrol_plugin_data() {
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
$cat = $this->getDataGenerator()->create_category();
|
||||
$cat1 = $this->getDataGenerator()->create_category(['parent' => $cat->id]);
|
||||
|
||||
@@ -519,6 +519,16 @@ class enrol_guest_plugin extends enrol_plugin {
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill custom fields data for a given enrolment plugin.
|
||||
*
|
||||
* @param array $enrolmentdata enrolment data.
|
||||
* @param int $courseid Course ID.
|
||||
* @return array Updated enrolment data with custom fields info.
|
||||
*/
|
||||
public function fill_enrol_custom_fields(array $enrolmentdata, int $courseid): array {
|
||||
return $enrolmentdata + ['password' => ''];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -675,6 +675,20 @@ class enrol_manual_plugin extends enrol_plugin {
|
||||
}
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill custom fields data for a given enrolment plugin.
|
||||
*
|
||||
* @param array $enrolmentdata enrolment data.
|
||||
* @param int $courseid Course ID.
|
||||
* @return array Updated enrolment data with custom fields info.
|
||||
*/
|
||||
public function fill_enrol_custom_fields(array $enrolmentdata, int $courseid): array {
|
||||
return $enrolmentdata + [
|
||||
'expirynotify' => 0,
|
||||
'expirythreshold' => 0,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+4
-1
@@ -468,7 +468,10 @@ class enrol_meta_plugin extends enrol_plugin {
|
||||
} else if (isset($enrolmentdata['groupname'])) {
|
||||
$enrolmentdata['customint2'] = groups_get_group_by_name($courseid, $enrolmentdata['groupname']);
|
||||
}
|
||||
return $enrolmentdata;
|
||||
return $enrolmentdata + [
|
||||
'customint1' => null,
|
||||
'customint2' => null,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1108,13 +1108,13 @@ class plugin_test extends \advanced_testcase {
|
||||
$enrolmentdata = $metaplugin->fill_enrol_custom_fields($enrolmentdata, $course1->id);
|
||||
$this->assertArrayHasKey('customint1', $enrolmentdata);
|
||||
$this->assertEquals($course2->id, $enrolmentdata['customint1']);
|
||||
$this->assertArrayNotHasKey('customint2', $enrolmentdata);
|
||||
$this->assertNull($enrolmentdata['customint2']);
|
||||
|
||||
$enrolmentdata['metacoursename'] = 'notexist';
|
||||
$enrolmentdata = $metaplugin->fill_enrol_custom_fields($enrolmentdata, $course1->id);
|
||||
$this->assertArrayHasKey('customint1', $enrolmentdata);
|
||||
$this->assertFalse($enrolmentdata['customint1']);
|
||||
$this->assertArrayNotHasKey('customint2', $enrolmentdata);
|
||||
$this->assertNull($enrolmentdata['customint2']);
|
||||
|
||||
$enrolmentdata['metacoursename'] = $course2->shortname;
|
||||
|
||||
|
||||
@@ -1110,6 +1110,16 @@ class enrol_self_plugin extends enrol_plugin {
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill custom fields data for a given enrolment plugin.
|
||||
*
|
||||
* @param array $enrolmentdata enrolment data.
|
||||
* @param int $courseid Course ID.
|
||||
* @return array Updated enrolment data with custom fields info.
|
||||
*/
|
||||
public function fill_enrol_custom_fields(array $enrolmentdata, int $courseid): array {
|
||||
return $enrolmentdata + ['password' => ''];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+27
-1
@@ -2707,6 +2707,9 @@ abstract class enrol_plugin {
|
||||
/**
|
||||
* Check if enrolment plugin is supported in csv course upload.
|
||||
*
|
||||
* If supported, plugins are also encouraged to override methods:
|
||||
* {@see self::fill_enrol_custom_fields()}, {@see self::validate_plugin_data_context()}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_csv_upload_supported(): bool {
|
||||
@@ -3485,7 +3488,10 @@ abstract class enrol_plugin {
|
||||
/**
|
||||
* Fill custom fields data for a given enrolment plugin.
|
||||
*
|
||||
* @param array $enrolmentdata enrolment data.
|
||||
* For example: resolve linked entities from the idnumbers (cohort, role, group, etc.)
|
||||
* Also fill the default values that are not specified.
|
||||
*
|
||||
* @param array $enrolmentdata enrolment data received in CSV file in tool_uploadcourse
|
||||
* @param int $courseid Course ID.
|
||||
* @return array Updated enrolment data with custom fields info.
|
||||
*/
|
||||
@@ -3514,11 +3520,31 @@ abstract class enrol_plugin {
|
||||
/**
|
||||
* Check if plugin custom data is allowed in relevant context.
|
||||
*
|
||||
* This is called from the tool_uploadcourse if the plugin supports instance creation in
|
||||
* upload course ({@see self::is_csv_upload_supported()})
|
||||
*
|
||||
* The fallback is to call the edit_instance_validation() but it will be better if the plugins
|
||||
* implement this method to return better error messages.
|
||||
*
|
||||
* @param array $enrolmentdata enrolment data to validate.
|
||||
* @param int|null $courseid Course ID.
|
||||
* @return lang_string|null Error
|
||||
*/
|
||||
public function validate_plugin_data_context(array $enrolmentdata, ?int $courseid = null) : ?lang_string {
|
||||
if ($courseid) {
|
||||
$enrolmentdata += ['courseid' => $courseid, 'id' => 0, 'status' => ENROL_INSTANCE_ENABLED];
|
||||
$instance = (object)[
|
||||
'id' => null,
|
||||
'courseid' => $courseid,
|
||||
'status' => $enrolmentdata['status'],
|
||||
'type' => $this->get_name(),
|
||||
];
|
||||
$formerrors = $this->edit_instance_validation($enrolmentdata, [], $instance, context_course::instance($courseid));
|
||||
if (!empty($formerrors)) {
|
||||
$errors = array_map(fn($key) => "{$key}: {$formerrors[$key]}", array_keys($formerrors));
|
||||
return new lang_string('errorcannotcreateorupdateenrolment', 'tool_uploadcourse', $errors);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user