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');
}
}
}