Permalink
Browse files

MDL-13114 tool_uploadcourse: Initial commit

  • Loading branch information...
1 parent 07bbbcf commit 3a0fda69d70fc0752adcd6bd1dd306c6c240fffc @piersharding piersharding committed with FMCorz Jun 18, 2013
View
196 admin/tool/uploadcourse/README.txt
@@ -0,0 +1,196 @@
+moodle-tool_uploadcourse
+========================
+
+Is a Moodle admin/tools plugin for uploading course outlines
+in much the same way that admin/tools/uploaduser works for users.
+These plugins became available from Moodle 2.2x and onwards, as
+this is when the admin/tools framework first appeared.
+
+https://gitorious.org/moodle-tool_uploadcourse
+
+There is also a bulk course category upload function available at https://gitorious.org/moodle-tool_uploadcoursecategory
+
+If you need to manage course enrolments via bulk upload then you should look at
+the core user upload facility - http://docs.moodle.org/22/en/Upload_users
+
+This takes CSV files as input and enables override or augmentation
+with default parameter values.
+
+All the usual add,updated,rename, and delete functions.
+
+
+Thanks to Moshe Golden for help with getting the command line interface going.
+
+
+!!!! NOTE !!!!
+===============
+
+This plugin used to come with the full directory structure
+admin/tool/uploadcourse - this is nolonger the case so the
+installation proceedure has changed!
+
+General installation proceedures are here:
+http://docs.moodle.org/20/en/Installing_contributed_modules_or_plugins
+
+The basic process is:
+Download https://gitorious.org/moodle-tool_uploadcourse/moodle-tool_uploadcourse/archive-tarball/master
+unpack the file (probably called master) with tar -xzvf master
+This will give you a directory called moodle-tool_uploadcourse-moodle-tool_uploadcourse
+Move this directory and rename it into it's final position:
+mv moodle-tool_uploadcourse-moodle-tool_uploadcourse <Moodle dirroot>/admin/tool/uploadcourse
+
+Alternatively you can use git:
+cd <Moodle dirroot>/admin/tool
+git clone git@gitorious.org:moodle-tool_uploadcourse/moodle-tool_uploadcourse.git uploadcourse
+
+Be careful about leaving the .git directory in your live environment.
+
+
+CSV File format
+===============
+
+Possible column names are:
+fullname, shortname, category, idnumber, summary,
+format, showgrades, newsitems, teacher, editingteacher, student, modinfo,
+manager, coursecreator, guest, user, startdate, numsections, maxbytes, visible, groupmode, restrictmodules,
+enablecompletion, completionstartonenrol, completionnotify, hiddensections, groupmodeforce, lang, theme,
+cost, showreports, notifystudents, expirynotify, expirythreshold, requested,
+deleted, // 1 means delete course
+oldshortname, // for renaming
+backupfile, // for restoring a course template after creation
+templatename, // course to use as a template - the shortname
+reset, // reset the course contents after upload - this resets everything
+ - so you loose groups, roles, logs, grades etc. Be Careful!!!
+
+An example file is:
+
+fullname,shortname,category,idnumber,summary,backupfile
+Computer Science 101,CS101,Cat1,CS101,The first thing you will ever know,/path/to/backup-moodle2-course-cs101-20120213-0748-nu.mbz
+
+As a general rule, the input values for fields are what you find on the data entry form if you inspect the HTML element.
+
+Format
+======
+The options for the format value are 'scorm', 'social', weeks', and 'topics'.
+
+Role Names
+===========
+ 'teacher', 'editingteacher', 'student', 'manager',
+'coursecreator', 'guest', 'user' are - where config permitting - you can
+substitute your own name for these roles (string value).
+
+Category
+========
+For category you must supply the category name as it is in Moodle and this
+field is case sensitive. If Sub Categories are involved then the full
+category hierarchy needs to be specified as a '/' delimited string eg:
+'Miscellaneous / Sub Cat / Sub Sub Cat'. The delimiter can be escaped with
+a back slash eg: 'some\/category'.
+
+Course Templating
+=================
+add column backupfile which has the fully qualified path name to a file on
+the server that has a a Moodle course backup in it.
+
+Add a column templatename which is the shortname of an existing course that
+will be copied over the top of the new course.
+
+Course Enrolment Methods
+=========================
+
+Enrolment methods need special CSV columns as there can be many per course, and the fields for each
+method are flexible. The following is an example with two enrolment methods - manual, and self - firstly you need
+the column identifying the enrolment method enrolmethod_<n>, and then add the corresponding field values subscripted with _<n>.
+eg:
+fullname,shortname,category,idnumber,summary,enrolmethod_1,status_1,enrolmethod_2,name_2,password_2,customtext1_2
+Parent,Parent,,Parent,Parent,manual,1,self,self1,letmein,this is a custom message 1
+Students,Students,,Students,Students,manual,0,self,self2,letmein,this is a custom message 2
+Teachers,Teachers,,Teachers,Teachers,manual,0,self,self3,letmein,this is a custom message 3
+
+add the special columns for:
+ * delete - delete_<n> with value 1
+ * disable - disable_<n> with value 1
+
+startdate enrol_startdate enrol_enddate
+=======================================
+For startdate enrolstartdate, and enrolenddate the values should be supplied in the form like 31.01.2012 or
+31/01/2012 that can be consumed by strtotime() (http://php.net/manual/en/function.strtotime.php) - check
+your PHP locale settings for the fine tuning eg: m/d/y vs d/m/y.
+
+Enrolment method field 'enrolperiod' must be in seconds. If this is supplied then enrolenddate will be calculated
+as enrolstartdate + enrolperiod.
+
+enrolperiod should be supplied in multiples of enrolment period measurements - 1 hour = 3600, 1 day = 86400
+and so on. OR - you can pass a text string that php strtotime() can recognise eg: '2 weeks' or '10 days'
+
+Enrolment Method Role
+=====================
+Default Role for an enrolment method is supplied by adding the 'role_<n>' column. The expected value is the
+descriptive label for the given role eg: 'Student', or "Teacher'.
+
+Enrolment example:
+fullname,shortname,category,idnumber,summary,enrolmethod_1,enrolperiod_1,role_1
+a name,short1,Miscellaneous,id1,a summary,manual,864000,Manager
+
+Update Course:
+=================
+Make sure you have shortname in the csv. After uploading the file, select:
+Upload type: one of the update existing related options
+Existing course details: Overide with file
+Allow Renames: Yes
+
+Update example:
+fullname,shortname
+new full name,short1
+
+
+Run it in batch mode
+=====================
+Execute Course Upload in batch mode - this must be run as the www-data user (or the equivalent user that the web server runs under).
+
+Options:
+-v, --verbose Print verbose progress information
+-h, --help Print out this help
+-a, --action Action to perform - addnew, addupdate, update, forceadd
+-m, --mode Mode of execution - delete, rename, nochange, file, filedefaults, missing
+-f, --file CSV File
+-d, --delimiter delimiter - colon,semicolon,tab,cfg,comma
+-e, --encoding File encoding - utf8 etc
+-c, --category Course category
+-s, --templateshortname Template course by shortname
+-t, --template Template course by backup file
+-g, --format Course format - weeks,scorm,social,topics
+-n, --numsections Number of sections
+
+
+Example:
+sudo -u www-data /usr/bin/php admin/tool/uploadcourse/cli/uploadcourse.php --action=addupdate \
+ --mode=delete --file=./courses.csv --delimiter=comma
+
+
+
+Installation
+=================
+git clone this repository into <moodle root>/admin/tools/uploadcourse directory.
+
+Point your browser at Moodle, and login as admin. This should kick off
+the upgrade so that Moodle can now recognise the new plugin.
+
+This was inspired in part by a need for a complimentary function for uploading
+courses (as for users) for the the NZ MLE tools for Identity and
+Access Managment (synchronising users with the School SMS):
+https://gitorious.org/pla-udi
+and
+https://gitorious.org/pla-udi/mle_ide_tools
+
+Copyright (C) Piers Harding 2011 and beyond, All rights reserved
+
+moodle-tool_uploadcourse free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This library 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
+Lesser General Public License for more details.
View
260 admin/tool/uploadcourse/cli/uploadcourse.php
@@ -0,0 +1,260 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * CLI Bulk course registration script from a comma separated file
+ *
+ * @package tool_uploadcourse
+ * @subpackage uploadcourse
+ * @copyright 2004 onwards Martin Dougiamas (http://dougiamas.com)
+ * @copyright 2012 Piers Harding
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ *
+ * Notes:
+ * - it is required to use the web server account when executing PHP CLI scripts
+ * - you need to change the "www-data" to match the apache user account
+ * - use "su" if "sudo" not available
+ *
+ */
+
+define('CLI_SCRIPT', true);
+
+require(dirname(dirname(dirname(dirname(dirname(__FILE__))))).'/config.php');
+
+require_once($CFG->libdir.'/adminlib.php');
+require_once($CFG->libdir.'/csvlib.class.php');
+require_once($CFG->dirroot.'/course/lib.php');
+require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
+require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
+require_once($CFG->libdir . '/filelib.php');
+require_once($CFG->libdir.'/clilib.php');
+require_once('../locallib.php');
+
+$courseconfig = get_config('moodlecourse');
+
+// Now get cli options.
+list($options, $unrecognized) = cli_get_params(
+ array('verbose' => false,
+ 'help' => false,
+ 'action' => '',
+ 'mode' => 'nochange',
+ 'file' => '',
+ 'delimiter' => 'comma',
+ 'encoding' => 'UTF-8',
+ 'category' => false,
+ 'templateshortname' => false,
+ 'template' => false,
+ 'format' => $courseconfig->format,
+ 'numsections' => $courseconfig->numsections,
+ 'reset' => false,
+ ),
+ array('v' => 'verbose',
+ 'h' => 'help',
+ 'a' => 'action',
+ 'm' => 'mode',
+ 'f' => 'file',
+ 'd' => 'delimiter',
+ 'e' => 'encoding',
+ 'c' => 'category',
+ 's' => 'templateshortname',
+ 't' => 'template',
+ 'g' => 'format',
+ 'n' => 'numsections',
+ 'r' => 'reset',
+ ));
+
+if ($unrecognized) {
+ $unrecognized = implode("\n ", $unrecognized);
+ cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
+}
+
+$help =
+"Execute Course Upload.
+
+Options:
+-v, --verbose Print verbose progress information
+-h, --help Print out this help
+-a, --action Action to perform - addnew, addupdate, update, forceadd
+-m, --mode Mode of execution - delete, rename, nochange, file, filedefaults, missing
+-f, --file CSV File
+-d, --delimiter delimiter - colon,semicolon,tab,cfg,comma
+-e, --encoding File encoding - utf8 etc
+-c, --category Course category
+-s, --templateshortname Template course by shortname
+-t, --template Template course by backup file
+-g, --format Course format - weeks,scorm,social,topics
+-n, --numsections Number of sections
+-r, --reset Run the course reset by default after each course import
+
+
+Example:
+\$sudo -u www-data /usr/bin/php admin/tool/uploadcourse/cli/uploadcourse.php --action=addupdate \\
+ --mode=delete --file=./courses.csv --delimiter=comma
+";
+
+if ($options['help']) {
+ echo $help;
+ die;
+}
+echo "Moodle course uploader running ...\n";
+
+$actions = array('addnew' => CC_COURSE_ADDNEW,
+ 'addupdate' => CC_COURSE_ADD_UPDATE,
+ 'update' => CC_COURSE_UPDATE,
+ 'forceadd' => CC_COURSE_ADDINC);
+if (!isset($options['action']) ||
+ !isset($actions[$options['action']]) ||
+ ($options['action'] != 'addnew' && !isset($options['mode']))) {
+ echo get_string('invalidinput', 'tool_uploadcourse')."\n";
+ echo $help;
+ die;
+}
+
+if (!empty($options['verbose']) || $CFG->debug) {
+ define('CC_DEBUG', true);
+}
+
+if (!isset($actions[$options['action']])) {
+ echo get_string('invalidaction', 'tool_uploadcourse')."\n";
+ echo $help;
+ die;
+}
+$options['cctype'] = $actions[$options['action']];
+
+$updatetype = array('nochange' => CC_UPDATE_NOCHANGES,
+ 'file' => CC_UPDATE_FILEOVERRIDE,
+ 'filedefaults' => CC_UPDATE_ALLOVERRIDE,
+ 'missing' => CC_UPDATE_MISSING);
+if ($options['mode'] == 'rename') {
+ $options['ccallowrenames'] = 1;
+ unset($options['mode']);
+} else if ($options['mode'] == 'delete') {
+ $options['ccallowdeletes'] = 1;
+ unset($options['mode']);
+} else if (!isset($updatetype[$options['mode']])) {
+ echo get_string('invalidmode', 'tool_uploadcourse')."\n";
+ echo $help;
+ die;
+}
+if (isset($options['mode'])) {
+ $options['ccupdatetype'] = $updatetype[$options['mode']];
+}
+$options['ccstandardshortnames'] = 1;
+$options['startdate'] = time() + 3600 * 24;
+$options['hiddensections'] = $courseconfig->hiddensections;
+$options['newsitems'] = $courseconfig->newsitems;
+$options['showgrades'] = $courseconfig->showgrades;
+$options['showreports'] = $courseconfig->showreports;
+$options['maxbytes'] = $courseconfig->maxbytes;
+$options['legacyfiles'] = 0;
+$options['groupmode'] = $courseconfig->groupmode;
+$options['groupmodeforce'] = $courseconfig->groupmodeforce;
+$options['visible'] = $courseconfig->visible;
+$options['lang'] = $courseconfig->lang;
+
+if ($options['category']) {
+ $split = preg_split('|(?<!\\\)/|', $options['category']);
+ $categories = array();
+ foreach ($split as $cat) {
+ $cat = preg_replace('/\\\/', '', $cat);
+ $categories[]= $cat;
+ }
+ $options['category'] = 0;
+ foreach ($categories as $cat) {
+ // Does the category exist - does the category hierachy make sense.
+ $category = $DB->get_record('course_categories', array('name'=>trim($cat), 'parent' => $options['category']));
+ if (empty($category)) {
+ echo get_string('invalidcategory', 'tool_uploadcourse')."\n";
+ echo $help;
+ die;
+ }
+ $options['category'] = $category->id;
+ }
+ $options['cccategory'] = $options['category'];
+} else {
+ $categories = $DB->get_records('course_categories');
+ if (empty($categories)) {
+ echo get_string('invalidcategory', 'tool_uploadcourse')."\n";
+ echo $help;
+ die;
+ }
+ $category = array_shift($categories);
+ $options['cccategory'] = $category->id;
+}
+
+if (isset($options['templateshortname'])) {
+ $options['ccshortname'] = $options['templateshortname'];
+}
+
+$options['file'] = realpath($options['file']);
+if (!file_exists($options['file'])) {
+ echo get_string('invalidcsvfile', 'tool_uploadcourse')."\n";
+ echo $help;
+ die;
+}
+
+$encodings = textlib::get_encodings();
+if (!isset($encodings[$options['encoding']])) {
+ echo get_string('invalidencoding', 'tool_uploadcourse')."\n";
+ echo $help;
+ die;
+}
+
+
+if ($options['template']) {
+ $options['template'] = realpath($options['template']);
+}
+if ($options['template'] && !file_exists($options['template'])) {
+ echo get_string('invalidtemplatefile', 'tool_uploadcourse')."\n";
+ echo $help;
+ die;
+}
+$tmpdir = $CFG->tempdir . '/backup';
+if (!check_dir_exists($tmpdir, true, true)) {
+ throw new restore_controller_exception('cannot_create_backup_temp_dir');
+}
+$filename = restore_controller::get_tempdir_name(SITEID, $USER->id);
+$restorefile = null;
+if ($options['template']) {
+ $restorefile = $options['template'];
+}
+
+$formdata = (object) $options;
+
+
+$returnurl = new moodle_url('/admin/tool/uploadcourse/index.php');
+$bulknurl = new moodle_url('/admin/tool/uploadcourse/index.php');
+$std_fields = tool_uploadcourse_std_fields();
+
+// Emulate normal session.
+cron_setup_user();
+
+$content = file_get_contents($formdata->file);
+$iid = csv_import_reader::get_new_iid('uploadcourse');
+$cir = new csv_import_reader($iid, 'uploadcourse');
+$readcount = $cir->load_csv_content($content, $formdata->encoding, $formdata->delimiter);
+$filecolumns = tool_uploadcourse_validate_course_upload_columns($cir, $std_fields, $returnurl);
+unset($content);
+if ($readcount === false) {
+ print_error('csvfileerror', 'tool_uploadcourse', $returnurl, $cir->get_error());
+} else if ($readcount == 0) {
+ print_error('csvemptyfile', 'error', $returnurl, $cir->get_error());
+}
+echo "CSV read count: ".$readcount."\n";
+
+$result = tool_uploadcourse_process_course_upload($formdata, $cir, $filecolumns, $restorefile, true);
+
+exit($result);
View
350 admin/tool/uploadcourse/course_form.php
@@ -0,0 +1,350 @@
+<?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 forms
+ *
+ * @package tool_uploadcourse
+ * @subpackage uploadcourse
+ * @copyright 2007 Dan Poltawski
+ * @copyright 2011 Piers Harding
+ * @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.
+ *
+ * @copyright 2007 Petr Skoda {@link http://skodak.org}
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class admin_uploadcourse_form1 extends moodleform {
+ /**
+ * The standard form definiton
+ * @return object $form
+ */
+ public function definition () {
+ $mform = $this->_form;
+
+ $mform->addElement('header', 'settingsheader', get_string('upload'));
+
+ $mform->addElement('filepicker', 'coursefile', get_string('file'));
+ $mform->addRule('coursefile', null, 'required');
+
+ $choices = csv_import_reader::get_delimiter_list();
+ $mform->addElement('select', 'delimiter_name', get_string('csvdelimiter', 'tool_uploadcourse'), $choices);
+ if (array_key_exists('cfg', $choices)) {
+ $mform->setDefault('delimiter_name', 'cfg');
+ } else if (get_string('listsep', 'langconfig') == ';') {
+ $mform->setDefault('delimiter_name', 'semicolon');
+ } else {
+ $mform->setDefault('delimiter_name', 'comma');
+ }
+
+ $choices = textlib::get_encodings();
+ $mform->addElement('select', 'encoding', get_string('encoding', 'tool_uploadcourse'), $choices);
+ $mform->setDefault('encoding', 'UTF-8');
+
+ $choices = array('10'=>10, '20'=>20, '100'=>100, '1000'=>1000, '100000'=>100000);
+ $mform->addElement('select', 'previewrows', get_string('rowpreviewnum', 'tool_uploadcourse'), $choices);
+ $mform->setType('previewrows', PARAM_INT);
+
+ $this->add_action_buttons(false, get_string('uploadcourses', 'tool_uploadcourse'));
+ }
+}
+
+
+/**
+ * Specify course upload details
+ *
+ * @copyright 2007 Petr Skoda {@link http://skodak.org}
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class admin_uploadcourse_form2 extends moodleform {
+ /**
+ * The standard form definiton
+ * @return object $form
+ */
+ public function definition () {
+ global $CFG, $COURSE, $DB;
+
+ $mform = $this->_form;
+ $columns = $this->_customdata['columns'];
+ $data = $this->_customdata['data'];
+ $courseconfig = get_config('moodlecourse');
+
+ // I am the template course, why should it be the administrator? we have roles now, other ppl may use this script ;-).
+ $templatecourse = $COURSE;
+
+ // Upload settings and file.
+ $mform->addElement('header', 'settingsheader', get_string('settings'));
+
+ $choices = array(CC_COURSE_ADDNEW => get_string('ccoptype_addnew', 'tool_uploadcourse'),
+ CC_COURSE_ADDINC => get_string('ccoptype_addinc', 'tool_uploadcourse'),
+ CC_COURSE_ADD_UPDATE => get_string('ccoptype_addupdate', 'tool_uploadcourse'),
+ CC_COURSE_UPDATE => get_string('ccoptype_update', 'tool_uploadcourse'));
+ $mform->addElement('select', 'cctype', get_string('ccoptype', 'tool_uploadcourse'), $choices);
+
+ $choices = array(CC_UPDATE_NOCHANGES => get_string('nochanges', 'tool_uploadcourse'),
+ CC_UPDATE_FILEOVERRIDE => get_string('ccupdatefromfile', 'tool_uploadcourse'),
+ CC_UPDATE_ALLOVERRIDE => get_string('ccupdateall', 'tool_uploadcourse'),
+ CC_UPDATE_MISSING => get_string('ccupdatemissing', 'tool_uploadcourse'));
+ $mform->addElement('select', 'ccupdatetype', get_string('ccupdatetype', 'tool_uploadcourse'), $choices);
+ $mform->setDefault('ccupdatetype', CC_UPDATE_NOCHANGES);
+ $mform->disabledIf('ccupdatetype', 'cctype', 'eq', CC_COURSE_ADDNEW);
+ $mform->disabledIf('ccupdatetype', 'cctype', 'eq', CC_COURSE_ADDINC);
+
+ $mform->addElement('selectyesno', 'ccallowrenames', get_string('allowrenames', 'tool_uploadcourse'));
+ $mform->setDefault('ccallowrenames', 0);
+ $mform->disabledIf('ccallowrenames', 'cctype', 'eq', CC_COURSE_ADDNEW);
+ $mform->disabledIf('ccallowrenames', 'cctype', 'eq', CC_COURSE_ADDINC);
+
+ $mform->addElement('selectyesno', 'ccallowdeletes', get_string('allowdeletes', 'tool_uploadcourse'));
+ $mform->setDefault('ccallowdeletes', 0);
+ $mform->disabledIf('ccallowdeletes', 'cctype', 'eq', CC_COURSE_ADDNEW);
+ $mform->disabledIf('ccallowdeletes', 'cctype', 'eq', CC_COURSE_ADDINC);
+
+ $mform->addElement('selectyesno', 'reset', get_string('reset', 'tool_uploadcourse'));
+ $mform->setDefault('ccallowdeletes', 0);
+ $mform->disabledIf('ccallowdeletes', 'cctype', 'eq', CC_COURSE_ADDNEW);
+ $mform->disabledIf('ccallowdeletes', 'cctype', 'eq', CC_COURSE_ADDINC);
+
+ $mform->addElement('selectyesno', 'ccstandardshortnames', get_string('ccstandardshortnames', 'tool_uploadcourse'));
+ $mform->setDefault('ccstandardshortnames', 1);
+
+ // Default values.
+ $mform->addElement('header', 'defaultheader', get_string('defaultvalues', 'tool_uploadcourse'));
+ $displaylist = array();
+ $parentlist = array();
+ make_categories_list($displaylist, $parentlist, 'moodle/course:create');
+ $mform->addElement('select', 'cccategory', get_string('category'), $displaylist);
+ $mform->addHelpButton('cccategory', 'category');
+
+ $mform->addElement('text', 'ccshortname', get_string('ccshortnametemplate', 'tool_uploadcourse'),
+ 'maxlength="100" size="20"');
+ $mform->addHelpButton('ccshortname', 'shortnamecourse', 'tool_uploadcourse');
+ $mform->disabledIf('ccshortname', 'cctype', 'eq', CC_COURSE_ADD_UPDATE);
+ $mform->disabledIf('ccshortname', 'cctype', 'eq', CC_COURSE_UPDATE);
+
+ $courseformats = get_plugin_list('format');
+ $formcourseformats = array();
+ foreach ($courseformats as $courseformat => $formatdir) {
+ $formcourseformats[$courseformat] = get_string('pluginname', "format_$courseformat");
+ }
+ $mform->addElement('select', 'format', get_string('format'), $formcourseformats);
+ $mform->addHelpButton('format', 'format');
+ $mform->setDefault('format', $courseconfig->format);
+
+ for ($i = 0; $i <= $courseconfig->maxsections; $i++) {
+ $sectionmenu[$i] = "$i";
+ }
+ $mform->addElement('select', 'numsections', get_string('numberweeks'), $sectionmenu);
+ $mform->setDefault('numsections', $courseconfig->numsections);
+
+ $mform->addElement('date_selector', 'startdate', get_string('startdate'));
+ $mform->addHelpButton('startdate', 'startdate');
+ $mform->setDefault('startdate', time() + 3600 * 24);
+
+ $choices = array();
+ $choices['0'] = get_string('hiddensectionscollapsed');
+ $choices['1'] = get_string('hiddensectionsinvisible');
+ $mform->addElement('select', 'hiddensections', get_string('hiddensections'), $choices);
+ $mform->addHelpButton('hiddensections', 'hiddensections');
+ $mform->setDefault('hiddensections', $courseconfig->hiddensections);
+
+ $options = range(0, 10);
+ $mform->addElement('select', 'newsitems', get_string('newsitemsnumber'), $options);
+ $mform->addHelpButton('newsitems', 'newsitemsnumber');
+ $mform->setDefault('newsitems', $courseconfig->newsitems);
+
+ $mform->addElement('selectyesno', 'showgrades', get_string('showgrades'));
+ $mform->addHelpButton('showgrades', 'showgrades');
+ $mform->setDefault('showgrades', $courseconfig->showgrades);
+
+ $mform->addElement('selectyesno', 'showreports', get_string('showreports'));
+ $mform->addHelpButton('showreports', 'showreports');
+ $mform->setDefault('showreports', $courseconfig->showreports);
+
+ $choices = get_max_upload_sizes($CFG->maxbytes);
+ $mform->addElement('select', 'maxbytes', get_string('maximumupload'), $choices);
+ $mform->addHelpButton('maxbytes', 'maximumupload');
+ $mform->setDefault('maxbytes', $courseconfig->maxbytes);
+
+ if (!empty($course->legacyfiles) or !empty($CFG->legacyfilesinnewcourses)) {
+ if (empty($course->legacyfiles)) {
+ // 0 or missing means no legacy files ever used in this course - new course or nobody turned on legacy files yet.
+ $choices = array('0'=>get_string('no'), '2'=>get_string('yes'));
+ } else {
+ $choices = array('1'=>get_string('no'), '2'=>get_string('yes'));
+ }
+ $mform->addElement('select', 'legacyfiles', get_string('courselegacyfiles'), $choices);
+ $mform->addHelpButton('legacyfiles', 'courselegacyfiles');
+ if (!isset($courseconfig->legacyfiles)) {
+ // In case this was not initialised properly due to switching of $CFG->legacyfilesinnewcourses.
+ $courseconfig->legacyfiles = 0;
+ }
+ $mform->setDefault('legacyfiles', $courseconfig->legacyfiles);
+ }
+
+ 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', 'theme', get_string('forcetheme'), $themes);
+ }
+ $courseshortnames = $DB->get_records('course', null, $sort='shortname', 'id,shortname,idnumber');
+ $formccourseshortnames = array('none' => get_string('none'));
+ foreach ($courseshortnames as $course) {
+ $formccourseshortnames[$course->shortname] = $course->shortname;
+ }
+ $mform->addElement('select', 'templatename', get_string('coursetemplatename', 'tool_uploadcourse'), $formccourseshortnames);
+ $mform->addHelpButton('templatename', 'coursetemplatename', 'tool_uploadcourse');
+ $mform->setDefault('templatename', 'none');
+
+ $contextid = $this->_customdata['contextid'];
+ $mform->addElement('hidden', 'contextid', $contextid);
+ $mform->addElement('filepicker', 'restorefile', get_string('templatefile', 'tool_uploadcourse'));
+
+ enrol_course_edit_form($mform, null, get_context_instance(CONTEXT_SYSTEM));
+
+ $mform->addElement('header', '', get_string('groups', 'group'));
+
+ $choices = array();
+ $choices[NOGROUPS] = get_string('groupsnone', 'group');
+ $choices[SEPARATEGROUPS] = get_string('groupsseparate', 'group');
+ $choices[VISIBLEGROUPS] = get_string('groupsvisible', 'group');
+ $mform->addElement('select', 'groupmode', get_string('groupmode', 'group'), $choices);
+ $mform->addHelpButton('groupmode', 'groupmode', 'group');
+ $mform->setDefault('groupmode', $courseconfig->groupmode);
+
+ $choices = array();
+ $choices['0'] = get_string('no');
+ $choices['1'] = get_string('yes');
+ $mform->addElement('select', 'groupmodeforce', get_string('groupmodeforce', 'group'), $choices);
+ $mform->addHelpButton('groupmodeforce', 'groupmodeforce', 'group');
+ $mform->setDefault('groupmodeforce', $courseconfig->groupmodeforce);
+
+ // Default groupings selector.
+ $options = array();
+ $options[0] = get_string('none');
+ $mform->addElement('select', 'defaultgroupingid', get_string('defaultgrouping', 'group'), $options);
+
+ $mform->addElement('header', '', get_string('availability'));
+
+ $choices = array();
+ $choices['0'] = get_string('courseavailablenot');
+ $choices['1'] = get_string('courseavailable');
+ $mform->addElement('select', 'visible', get_string('availability'), $choices);
+ $mform->addHelpButton('visible', 'availability');
+ $mform->setDefault('visible', $courseconfig->visible);
+
+ $mform->addElement('header', '', get_string('language'));
+
+ $languages=array();
+ $languages[''] = get_string('forceno');
+ $languages += get_string_manager()->get_list_of_translations();
+ $mform->addElement('select', 'lang', get_string('forcelanguage'), $languages);
+ $mform->setDefault('lang', $courseconfig->lang);
+
+ // Hidden fields.
+ $mform->addElement('hidden', 'iid');
+ $mform->setType('iid', PARAM_INT);
+
+ $mform->addElement('hidden', 'previewrows');
+ $mform->setType('previewrows', PARAM_INT);
+
+ $this->add_action_buttons(true, get_string('uploadcourses', 'tool_uploadcourse'));
+
+ $this->set_data($data);
+ }
+
+ /**
+ * Form tweaks that depend on current data.
+ */
+ public function definition_after_data() {
+ $mform = $this->_form;
+ $columns = $this->_customdata['columns'];
+
+ foreach ($columns as $column) {
+ if ($mform->elementExists($column)) {
+ $mform->removeElement($column);
+ }
+ }
+
+ }
+
+ /**
+ * Server side validation.
+ * @param array $data - form data
+ * @param object $files - form files
+ * @return array $errors - form errors
+ */
+ public function validation($data, $files) {
+ global $DB;
+
+ $errors = parent::validation($data, $files);
+ $columns = $this->_customdata['columns'];
+ $optype = $data['cctype'];
+
+ // Look for other required data.
+ if ($optype != CC_COURSE_UPDATE) {
+ if (!in_array('fullname', $columns)) {
+ if (isset($errors['cctype'])) {
+ $errors['cctype'] .= ' ';
+ }
+ $errors['cctype'] .= get_string('missingfield', 'error', 'fullname');
+ }
+ if (!in_array('summary', $columns)) {
+ if (isset($errors['cctype'])) {
+ $errors['cctype'] .= ' ';
+ }
+ $errors['cctype'] .= get_string('missingfield', 'error', 'summary');
+ }
+ }
+ if (!empty($data['templatename']) && $data['templatename'] != 'none') {
+ if (!$template = $DB->get_record('course', array('shortname' => $data['templatename']))) {
+ $errors['templatename'] = get_string('missingtemplate', 'tool_uploadcourse');
+ }
+ }
+
+ return $errors;
+ }
+
+ /**
+ * Used to reformat the data from the editor component
+ *
+ * @return stdClass
+ */
+ public function get_data() {
+ $data = parent::get_data();
+
+ if ($data !== null and isset($data->description)) {
+ $data->descriptionformat = $data->description['format'];
+ $data->description = $data->description['text'];
+ }
+
+ return $data;
+ }
+}
View
184 admin/tool/uploadcourse/index.php
@@ -0,0 +1,184 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Bulk course registration script from a comma separated file
+ *
+ * @package tool_uploadcourse
+ * @copyright 2004 onwards Martin Dougiamas (http://dougiamas.com)
+ * @copyright 2011 Piers Harding
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require('../../../config.php');
+require_once($CFG->libdir.'/adminlib.php');
+require_once($CFG->libdir.'/csvlib.class.php');
+require_once($CFG->dirroot.'/course/lib.php');
+require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
+require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
+require_once($CFG->libdir . '/filelib.php');
+require_once('locallib.php');
+require_once('course_form.php');
+
+$iid = optional_param('iid', '', PARAM_INT);
+$previewrows = optional_param('previewrows', 10, PARAM_INT);
+require_login();
+admin_externalpage_setup('tooluploadcourse');
+
+$returnurl = new moodle_url('/admin/tool/uploadcourse/index.php');
+$bulknurl = new moodle_url('/admin/tool/uploadcourse/index.php');
+$std_fields = tool_uploadcourse_std_fields();
+
+
+if (empty($iid)) {
+ $mform1 = new admin_uploadcourse_form1();
+
+ if ($formdata = $mform1->get_data()) {
+ $iid = csv_import_reader::get_new_iid('uploadcourse');
+ $cir = new csv_import_reader($iid, 'uploadcourse');
+
+ $content = $mform1->get_file_content('coursefile');
+
+ $readcount = $cir->load_csv_content($content, $formdata->encoding, $formdata->delimiter_name);
+ unset($content);
+
+ if ($readcount === false) {
+ print_error('csvfileerror', 'tool_uploadcourse', $returnurl, $cir->get_error());
+ } else if ($readcount == 0) {
+ print_error('csvemptyfile', 'error', $returnurl, $cir->get_error());
+ }
+ // Test if columns ok.
+ $filecolumns = tool_uploadcourse_validate_course_upload_columns($cir, $std_fields, $returnurl);
+ // Continue to form2.
+
+ } else {
+ echo $OUTPUT->header();
+
+ echo $OUTPUT->heading_with_help(get_string('uploadcourses', 'tool_uploadcourse'), 'uploadcourses', 'tool_uploadcourse');
+
+ $mform1->display();
+ echo $OUTPUT->footer();
+ die;
+ }
+} else {
+ $cir = new csv_import_reader($iid, 'uploadcourse');
+ $filecolumns = tool_uploadcourse_validate_course_upload_columns($cir, $std_fields, $returnurl);
+}
+
+$frontpagecontext = context_course::instance(SITEID);
+$mform2 = new admin_uploadcourse_form2(null,
+ array('contextid' => $frontpagecontext->id,
+ 'columns' => $filecolumns,
+ 'data' => array('iid'=>$iid, 'previewrows'=>$previewrows)));
+
+// If a file has been uploaded, then process it.
+if ($formdata = $mform2->is_cancelled()) {
+ $cir->cleanup(true);
+ redirect($returnurl);
+} else if ($formdata = $mform2->get_data()) {
+ // Print the header.
+ echo $OUTPUT->header();
+ echo $OUTPUT->heading(get_string('uploadcoursesresult', 'tool_uploadcourse'));
+
+ $tmpdir = $CFG->tempdir . '/backup';
+ if (!check_dir_exists($tmpdir, true, true)) {
+ throw new restore_controller_exception('cannot_create_backup_temp_dir');
+ }
+ $filename = restore_controller::get_tempdir_name(SITEID, $USER->id);
+ $restorefile = $tmpdir . '/' . $filename;
+ if (!$mform2->save_file('restorefile', $restorefile)) {
+ $restorefile = null;
+ }
+ $bulk = isset($formdata->ccbulk) ? $formdata->ccbulk : 0;
+
+ tool_uploadcourse_process_course_upload($formdata, $cir, $filecolumns, $restorefile);
+
+ echo $OUTPUT->box_end();
+
+ if ($bulk) {
+ echo $OUTPUT->continue_button($bulknurl);
+ } else {
+ echo $OUTPUT->continue_button($returnurl);
+ }
+ echo $OUTPUT->footer();
+ die;
+}
+
+// Print the header.
+echo $OUTPUT->header();
+
+echo $OUTPUT->heading(get_string('uploadcoursespreview', 'tool_uploadcourse'));
+
+// NOTE: this is JUST csv processing preview, we must not prevent import from here if there is something in the file!!
+// this was intended for validation of csv formatting and encoding, not filtering the data!!!!
+// we definitely must not process the whole file!
+
+// Preview table data.
+$data = array();
+$cir->init();
+$linenum = 1; // Column header is first line.
+while ($linenum <= $previewrows and $fields = $cir->next()) {
+ $linenum++;
+ $rowcols = array();
+ $rowcols['line'] = $linenum;
+ foreach ($fields as $key => $field) {
+ $rowcols[$filecolumns[$key]] = s($field);
+ }
+ $rowcols['status'] = array();
+
+ if (isset($rowcols['shortname'])) {
+ $stdshortname = clean_param($rowcols['shortname'], PARAM_MULTILANG);
+ if ($rowcols['shortname'] !== $stdshortname) {
+ $rowcols['status'][] = get_string('invalidshortnameupload');
+ }
+ if ($courseid = $DB->get_field('course', 'id', array('shortname'=>$stdshortname))) {
+ $rowcols['shortname'] = html_writer::link(new moodle_url('/course/view.php',
+ array('id' => $courseid)),
+ $rowcols['shortname']);
+ }
+ } else {
+ $rowcols['status'][] = get_string('missingshortname');
+ }
+
+ $rowcols['status'] = implode('<br />', $rowcols['status']);
+ $data[] = $rowcols;
+}
+if ($fields = $cir->next()) {
+ $data[] = array_fill(0, count($fields) + 2, '...');
+}
+$cir->close();
+
+$table = new html_table();
+$table->id = "ccpreview";
+$table->attributes['class'] = 'generaltable';
+$table->tablealign = 'center';
+$table->summary = get_string('uploadcoursespreview', 'tool_uploadcourse');
+$table->head = array();
+$table->data = $data;
+
+$table->head[] = get_string('cccsvline', 'tool_uploadcourse');
+foreach ($filecolumns as $column) {
+ $table->head[] = $column;
+}
+$table->head[] = get_string('status');
+
+echo html_writer::tag('div', html_writer::table($table), array('class'=>'flexible-wrap'));
+
+// Print the form.
+$mform2->display();
+echo $OUTPUT->footer();
+die;
+
View
130 admin/tool/uploadcourse/lang/en/tool_uploadcourse.php
@@ -0,0 +1,130 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Strings for component 'tool_uploadcourse', language 'en', branch 'MOODLE_22_STABLE'
+ *
+ * @package tool_uploadcourse
+ * @subpackage uploadcourse
+ * @copyright 2011 Petr Skoda {@link http://skodak.org}
+ * @copyright 2011 Piers Harding
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['csvfileerror'] = 'There is something wrong with the format of the CSV file - please check the number of headings and columns match, and that the delimiter and file encoding are correct (don\t use comma-quoted as Moodle does not support it): {$a}';
+$string['allowdeletes'] = 'Allow deletes';
+$string['allowrenames'] = 'Allow renames';
+$string['csvdelimiter'] = 'CSV delimiter';
+$string['defaultvalues'] = 'Default values';
+$string['deleteerrors'] = 'Delete errors';
+$string['encoding'] = 'Encoding';
+$string['errors'] = 'Errors';
+$string['invalidinput'] = 'You must specify a valid combination of --action and --mode';
+$string['nochanges'] = 'No changes';
+$string['pluginname'] = 'Course upload';
+$string['renameerrors'] = 'Rename errors';
+$string['requiredtemplate'] = 'Required. You may use template syntax here (%l = lastname, %f = firstname, %u = coursename). See help for details and examples.';
+$string['rowpreviewnum'] = 'Preview rows';
+$string['uploadpicture_badcoursefield'] = 'The course attribute specified is not valid. Please, try again.';
+$string['uploadpicture_cannotmovezip'] = 'Cannot move zip file to temporary directory.';
+$string['uploadpicture_cannotprocessdir'] = 'Cannot process unzipped files.';
+$string['uploadpicture_cannotsave'] = 'Cannot save picture for course {$a}. Check original picture file.';
+$string['uploadpicture_cannotunzip'] = 'Cannot unzip pictures file.';
+$string['uploadpicture_invalidfilename'] = 'Picture file {$a} has invalid characters in its name. Skipping.';
+$string['uploadpicture_overwrite'] = 'Overwrite existing course pictures?';
+$string['uploadpicture_coursefield'] = 'Course attribute to use to match pictures:';
+$string['uploadpicture_coursenotfound'] = 'Course with a \'{$a->coursefield}\' value of \'{$a->coursevalue}\' does not exist. Skipping.';
+$string['uploadpicture_courseskipped'] = 'Skipping course {$a} (already has a picture).';
+$string['uploadpicture_courseupdated'] = 'Picture updated for course {$a}.';
+$string['uploadpictures'] = 'Upload course pictures';
+$string['uploadpictures_help'] = 'Course pictures can be uploaded as a zip file of image files. The image files should be named chosen-course-attribute.extension, for example course1234.jpg for a course with coursename course1234.';
+$string['uploadcourses'] = 'Upload courses';
+$string['uploadcourses_help'] = 'Courses may be uploaded (and optionally enrolled in courses) via text file. The format of the file should be as follows:
+
+* Each line of the file contains one record
+* Each record is a series of data separated by commas (or other delimiters)
+* The first record contains a list of fieldnames defining the format of the rest of the file
+* Required fieldnames are coursename, password, firstname, lastname, email';
+$string['uploadcoursespreview'] = 'Upload courses preview';
+$string['uploadcoursesresult'] = 'Upload courses results';
+$string['courseupdated'] = 'Course updated';
+$string['courseuptodate'] = 'Course up-to-date';
+$string['coursedeleted'] = 'Course deleted';
+$string['courserenamed'] = 'Course renamed';
+$string['coursescreated'] = 'Courses created';
+$string['coursesdeleted'] = 'Courses deleted';
+$string['coursesrenamed'] = 'Courses renamed';
+$string['coursesskipped'] = 'Courses skipped';
+$string['coursesupdated'] = 'Courses updated';
+$string['coursenotadded'] = 'Course not added - already exists';
+$string['coursenotaddederror'] = 'Course not added - error';
+$string['coursenotdeletederror'] = 'Course not deleted - error';
+$string['coursenotdeletedmissing'] = 'Course not deleted - missing';
+$string['coursenotdeletedoff'] = 'Course not deleted - delete off';
+$string['coursenotdeletedadmin'] = 'Course not deleted - no admin access';
+$string['coursenotupdatederror'] = 'Course not updated - error';
+$string['coursenotupdatednotexists'] = 'Course not updated - does not exist';
+$string['coursenotupdatedadmin'] = 'Course not updated - no admin';
+$string['coursenotrenamedexists'] = 'Course not renamed - target exists';
+$string['coursenotrenamedmissing'] = 'Course not renamed - source missing';
+$string['coursenotrenamedoff'] = 'Course not renamed - renaming off';
+$string['coursenotrenamedadmin'] = 'Course not renamed - no admin';
+$string['invalidvalue'] = 'Invalid value for field {$a}';
+$string['shortnamecourse'] = 'Shortname';
+$string['shortnamecourse_help'] = 'The short name of the course is displayed in the navigation. You may use template syntax here (%f = fullname, %i = idnumber), or enter an initial value that is incremented. See help for details and examples.';
+$string['idnumbernotunique'] = 'idnumber is not unique';
+$string['ccbulk'] = 'Select for bulk operations';
+$string['ccbulkall'] = 'All courses';
+$string['ccbulknew'] = 'New courses';
+$string['ccbulkupdated'] = 'Updated courses';
+$string['cccsvline'] = 'CSV line';
+$string['cclegacy1role'] = '(Original Student) typeN=1';
+$string['cclegacy2role'] = '(Original Teacher) typeN=2';
+$string['cclegacy3role'] = '(Original Non-editing teacher) typeN=3';
+$string['ccnoemailduplicates'] = 'Prevent email address duplicates';
+$string['ccoptype'] = 'Upload type';
+$string['ccoptype_addinc'] = 'Add all, append number to shortnames if needed';
+$string['ccoptype_addnew'] = 'Add new only, skip existing courses';
+$string['ccoptype_addupdate'] = 'Add new and update existing courses';
+$string['ccoptype_update'] = 'Update existing courses only';
+$string['ccpasswordcron'] = 'Generated in cron';
+$string['ccpasswordnew'] = 'New course password';
+$string['ccpasswordold'] = 'Existing course password';
+$string['reset'] = 'Reset course after upload';
+$string['ccstandardshortnames'] = 'Standardise shortnames';
+$string['ccupdateall'] = 'Override with file and defaults';
+$string['ccupdatefromfile'] = 'Override with file';
+$string['ccupdatemissing'] = 'Fill in missing from file and defaults';
+$string['ccupdatetype'] = 'Existing course details';
+$string['ccshortnametemplate'] = 'Shortname template';
+$string['ccfullnametemplate'] = 'Fullname template';
+$string['ccidnumbertemplate'] = 'Idnumber template';
+$string['missingtemplate'] = 'Template not found';
+$string['missing'] = 'missing';
+$string['incorrectformat'] = 'Invalid format specified';
+$string['incorrecttemplatefile'] = 'Template file not found';
+$string['invalidenrolmethod'] = 'Invalid enrolment method';
+$string['invalidcsvfile'] = 'Invalid input CSV file';
+$string['invalidaction'] = 'Invalid action selected';
+$string['invalidmode'] = 'Invalid mode selected';
+$string['invalidtemplatefile'] = 'Invalid template file';
+$string['invalidencoding'] = 'Invalid encoding';
+$string['invalidcategory'] = 'Invalid category';
+$string['coursetemplatename'] = 'Course template shortname';
+$string['coursetemplatename_help'] = 'Select an existing course shortname to use as a template for the creation of all courses.';
+$string['templatefile'] = 'Template backup file';
+$string['invalidbackupfile'] = 'Invalid backup file';
+
View
1,328 admin/tool/uploadcourse/locallib.php
@@ -0,0 +1,1328 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Bulk course registration functions
+ *
+ * @package tool_uploadcourse
+ * @subpackage uploadcourse
+ * @copyright 2004 onwards Martin Dougiamas (http://dougiamas.com)
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+define('CC_COURSE_ADDNEW', 0);
+define('CC_COURSE_ADDINC', 1);
+define('CC_COURSE_ADD_UPDATE', 2);
+define('CC_COURSE_UPDATE', 3);
+
+define('CC_UPDATE_NOCHANGES', 0);
+define('CC_UPDATE_FILEOVERRIDE', 1);
+define('CC_UPDATE_ALLOVERRIDE', 2);
+define('CC_UPDATE_MISSING', 3);
+
+define('CC_BULK_NEW', 1);
+define('CC_BULK_UPDATED', 2);
+define('CC_BULK_ALL', 3);
+
+define('CC_PWRESET_NONE', 0);
+define('CC_PWRESET_WEAK', 1);
+define('CC_PWRESET_ALL', 2);
+define('CC_BULK_NONE', 0);
+
+
+/**
+ * Return the list of stad fields the course upload processes
+ */
+function tool_uploadcourse_std_fields() {
+ // Array of all valid fields for validation.
+ return $std_fields = array('fullname', 'shortname', 'category', 'idnumber', 'summary',
+ 'format', 'showgrades', 'newsitems', 'teacher', 'editingteacher', 'student', 'modinfo',
+ 'manager', 'coursecreator', 'guest', 'user', 'startdate', 'numsections',
+ 'maxbytes', 'visible', 'groupmode', 'restrictmodules',
+ 'enablecompletion', 'completionstartonenrol', 'completionnotify',
+ 'hiddensections', 'groupmodeforce', 'lang', 'theme',
+ 'cost', 'showreports', 'notifystudents', 'expirynotify', 'expirythreshold', 'requested',
+ 'deleted', // 1 means delete course.
+ 'oldshortname', // For renaming.
+ 'backupfile', // For restoring a course template after creation.
+ 'templatename', // Course to use as a template - the shortname.
+ 'reset',
+ // There are also the enrolment fields but these are free form as they vary on enrolment type
+ // eg: enrolmethod_1,status_1,enrolmethod_2,name_2,password_2,customtext1_2
+ // manual, 1, self, self1, letmein, this is a custom message 1.
+ );
+}
+
+/**
+ * process the upload
+ *
+ * @param object $formdata - object of the form data
+ * @param object $cir - object of the CSV importer
+ * @param array $filecolumns - file column definitions
+ * @param string $restorefile - file to restore from
+ * @param boolean $plain - plain text output
+ */
+function tool_uploadcourse_process_course_upload($formdata, $cir, $filecolumns, $restorefile=null, $plain=false) {
+ global $CFG, $USER, $OUTPUT, $SESSION, $DB;
+
+ $std_fields = tool_uploadcourse_std_fields();
+
+ @set_time_limit(60*60); // 1 hour should be enough.
+ raise_memory_limit(MEMORY_HUGE);
+
+ require_capability('moodle/course:create', get_context_instance(CONTEXT_SYSTEM));
+ require_capability('moodle/course:update', get_context_instance(CONTEXT_SYSTEM));
+ require_capability('moodle/course:delete', get_context_instance(CONTEXT_SYSTEM));
+
+ $strcourserenamed = get_string('courserenamed', 'tool_uploadcourse');
+ $strcoursenotrenamedexists = get_string('coursenotrenamedexists', 'tool_uploadcourse');
+ $strcoursenotrenamedmissing = get_string('coursenotrenamedmissing', 'tool_uploadcourse');
+ $strcoursenotrenamedoff = get_string('coursenotrenamedoff', 'tool_uploadcourse');
+
+ $strcourseupdated = get_string('courseupdated', 'tool_uploadcourse');
+ $strcoursenotupdated = get_string('coursenotupdatederror', 'tool_uploadcourse');
+ $strcoursenotupdatednotexists = get_string('coursenotupdatednotexists', 'tool_uploadcourse');
+
+ $strcourseuptodate = get_string('courseuptodate', 'tool_uploadcourse');
+
+ $strcourseadded = get_string('newcourse');
+ $strcoursenotadded = get_string('coursenotadded', 'tool_uploadcourse');
+ $strcoursenotaddederror = get_string('coursenotaddederror', 'tool_uploadcourse');
+
+ $strcoursedeleted = get_string('coursedeleted', 'tool_uploadcourse');
+ $strcoursenotdeletederror = get_string('coursenotdeletederror', 'tool_uploadcourse');
+ $strcoursenotdeletedmissing = get_string('coursenotdeletedmissing', 'tool_uploadcourse');
+ $strcoursenotdeletedoff = get_string('coursenotdeletedoff', 'tool_uploadcourse');
+ $errorstr = get_string('error');
+
+ $returnurl = new moodle_url('/admin/tool/uploadcourse/index.php');
+ $bulknurl = new moodle_url('/admin/tool/uploadcourse/index.php');
+
+ $today = time();
+ $today = make_timestamp(date('Y', $today), date('m', $today), date('d', $today), 0, 0, 0);
+
+ $optype = $formdata->cctype;
+
+ $updatetype = isset($formdata->ccupdatetype) ? $formdata->ccupdatetype : 0;
+ $allowrenames = (!empty($formdata->ccallowrenames) and $optype != CC_COURSE_ADDNEW and $optype != CC_COURSE_ADDINC);
+ $allowdeletes = (!empty($formdata->ccallowdeletes) and $optype != CC_COURSE_ADDNEW and $optype != CC_COURSE_ADDINC);
+ $bulk = isset($formdata->ccbulk) ? $formdata->ccbulk : 0;
+ $standardshortnames = $formdata->ccstandardshortnames;
+
+ // Check for the template.
+ $templatepathname = null;
+ if (!empty($formdata->templatename) && $formdata->templatename != 'none') {
+ $template = $DB->get_record('course', array('shortname' => $formdata->templatename));
+
+ // Backup the course template.
+ $bc = new backup_controller(backup::TYPE_1COURSE, $template->id, backup::FORMAT_MOODLE,
+ backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id);
+ $backupid = $bc->get_backupid();
+ $backupbasepath = $bc->get_plan()->get_basepath();
+ $bc->execute_plan();
+ $bc->destroy();
+ $packer = get_file_packer('application/zip');
+ // Check if tmp dir exists.
+ $tmpdir = $CFG->tempdir . '/backup';
+ if (!check_dir_exists($tmpdir, true, true)) {
+ throw new restore_controller_exception('cannot_create_backup_temp_dir');
+ }
+ $filename = restore_controller::get_tempdir_name(SITEID, $USER->id);
+ $templatepathname = $tmpdir . '/' . $filename;
+ // Get the list of files in directory.
+ $filestemp = get_directory_list($backupbasepath, '', false, true, true);
+ $files = array();
+ foreach ($filestemp as $file) {
+ // Add zip paths and fs paths to all them.
+ $files[$file] = $backupbasepath . '/' . $file;
+ }
+ $zippacker = get_file_packer('application/zip');
+ $zippacker->archive_to_pathname($files, $templatepathname);
+ if (empty($CFG->keeptempdirectoriesonbackup)) {
+ fulldelete($backupbasepath);
+ }
+ }
+
+ // Check the uploaded backup file.
+ if (!empty($formdata->restorefile)) {
+ // Check if tmp dir exists.
+ if ($restorefile) {
+ $filepath = restore_controller::get_tempdir_name(SITEID, $USER->id);
+ $packer = get_file_packer('application/zip');
+ $restorepathname = "$CFG->tempdir/backup/$filepath/";
+ $result = $packer->extract_to_pathname($restorefile, $restorepathname);
+ // If not a backup zip file.
+ if (!$result) {
+ if (empty($CFG->keeptempdirectoriesonbackup)) {
+ fulldelete($restorepathname);
+ fulldelete($restorefile);
+ }
+ throw new moodle_exception('invalidbackupfile', 'tool_uploadcourse');
+ }
+ if (empty($CFG->keeptempdirectoriesonbackup)) {
+ fulldelete($restorepathname);
+ }
+ } else {
+ $restorefile = null;
+ }
+ }
+
+ // Verification moved to two places: after upload and into form2.
+ $coursesnew = 0;
+ $coursesupdated = 0;
+ $coursesuptodate = 0; // Not printed yet anywhere.
+ $courseserrors = 0;
+ $deletes = 0;
+ $deleteerrors = 0;
+ $renames = 0;
+ $renameerrors = 0;
+ $coursesskipped = 0;
+ $enrolmentplugins = enrol_get_plugins(false);
+ $courseformats = array_keys(get_plugin_list('format'));
+
+ // Clear bulk selection.
+ if ($bulk) {
+ $SESSION->bulk_courses = array();
+ }
+
+ // Init csv import helper.
+ $cir->init();
+ $linenum = 1; // Column header is first line.
+
+ // Init upload progress tracker.
+ $upt = new tool_uploadcourse_progress_tracker($plain);
+ $upt->start(); // Start table.
+
+ while ($line = $cir->next()) {
+ $upt->flush();
+ $linenum++;
+
+ $upt->track('line', $linenum);
+
+ $course = new stdClass();
+
+ // Add fields to course object.
+ foreach ($line as $keynum => $value) {
+ if (!isset($filecolumns[$keynum])) {
+ // This should not happen.
+ continue;
+ }
+ $key = $filecolumns[$keynum];
+ $course->$key = $value;
+
+ if (in_array($key, $upt->columns)) {
+ // Default value in progress tracking table, can be changed later.
+ $upt->track($key, s($value), 'normal');
+ }
+ }
+ // Validate category.
+ $error = false;
+ if (!empty($course->category)) {
+ $split = preg_split('|(?<!\\\)/|', $course->category);
+ $categories = array();
+ foreach ($split as $cat) {
+ $cat = preg_replace('/\\\/', '', $cat);
+ $categories[]= $cat;
+ }
+ $course->category = 0;
+ foreach ($categories as $cat) {
+ // Does the category exist - does the category hierachy make sense.
+ $category = $DB->get_record('course_categories', array('name'=>trim($cat), 'parent' => $course->category));
+ if (empty($category)) {
+ $upt->track('status', get_string('invalidvalue', 'tool_uploadcourse', 'category').' ('.$cat.' '.get_string('missing', 'tool_uploadcourse').')', 'error');
+ $upt->track('category', $errorstr, 'error');
+ $error = true;
+ break;
+ }
+ $course->category = $category->id;
+ }
+ }
+ // Check for category errors.
+ if ($error) {
+ $courseserrors++;
+ continue;
+ }
+
+ if (!isset($course->shortname)) {
+ // Prevent warnings bellow.
+ $course->shortname = '';
+ }
+ if (!empty($course->startdate) && $course->startdate != 0) {
+ $course->startdate = strtotime($course->startdate);
+ }
+ if (!empty($course->enrolstartdate) && $course->enrolstartdate != 0) {
+ $course->enrolstartdate = strtotime($course->enrolstartdate);
+ }
+
+ // Check for enrolment methods.
+ $line_fields = (array) $course;
+ $enrolmethods = array();
+ $enrolments = array();
+ $error = false;
+ foreach ($line_fields as $k => $v) {
+ if (preg_match('/^(\w+)\_(\d+)$/', $k, $matches)) {
+ if (!isset($enrolments[$matches[2]])) {
+ $enrolments[$matches[2]] = array();
+ }
+ if ($matches[1] == 'enrolmethod') {
+ if (!isset($enrolmentplugins[$v])) {
+ $upt->track('status', get_string('invalidenrolmethod', 'tool_uploadcourse', 'category'), 'error');
+ $upt->track($k, $errorstr, 'error');
+ $error = true;
+ }
+ $enrolmethods[$v] = $matches[2];
+ }
+ $enrolments[$matches[2]][$matches[1]] = $v;
+ }
+ }
+ if ($error) {
+ continue;
+ }
+ foreach ($enrolmethods as $k => $v) {
+ $enrolmethods[$k] = $enrolments[$v];
+ }
+
+ // Roles.
+ $roles = get_all_roles();
+ foreach ($roles as $role) {
+ if (isset($course->{$role->shortname})) {
+ if (in_array($role->shortname, array('teacher', 'editingteacher', 'student',
+ 'manager', 'coursecreator', 'guest', 'user'))) {
+ $course->{'role_'.$role->id} = $course->{$role->shortname};
+ }
+ }
+ }
+
+ // What type of operation is this ?
+ if ($optype == CC_COURSE_ADDNEW or $optype == CC_COURSE_ADDINC) {
+ // Course creation is a special case - the shortname may be constructed from templates using firstname and lastname
+ // better never try this in mixed update types.
+ $error = false;
+ if (!isset($course->fullname) or $course->fullname === '') {
+ $upt->track('status', get_string('missingfield', 'error', 'fullname'), 'error');
+ $upt->track('fullname', $errorstr, 'error');
+ $error = true;
+ }
+ if (!isset($course->summary) or $course->summary === '') {
+ $upt->track('status', get_string('missingfield', 'error', 'summary'), 'error');
+ $upt->track('summary', $errorstr, 'error');
+ $error = true;
+ }
+ if ($error) {
+ $courseserrors++;
+ continue;
+ }
+ // We require shortname too - we might use template for it though.
+ if (empty($course->shortname) and !empty($formdata->ccshortname)) {
+ $course->shortname = tool_uploadcourse_process_template($formdata->ccshortname, $course);
+ $upt->track('shortname', s($course->shortname));
+ }
+ }
+
+ // Normalize shortname.
+ $originalshortname = $course->shortname;
+ if ($standardshortnames) {
+ $course->shortname = clean_param($course->shortname, PARAM_MULTILANG);
+ }
+
+ // Make sure we really have shortname.
+ if (empty($course->shortname)) {
+ $upt->track('status', get_string('missingfield', 'error', 'shortname'), 'error');
+ $upt->track('shortname', $errorstr, 'error');
+ $courseserrors++;
+ continue;
+ }
+
+ if ($existingcourse = $DB->get_record('course', array('shortname' => $course->shortname))) {
+ $upt->track('id', $existingcourse->id, 'normal', false);
+ }
+
+ // Find out in shortname incrementing required.
+ if ($existingcourse and $optype == CC_COURSE_ADDINC) {
+ $course->shortname = tool_uploadcourse_increment_shortname($course->shortname);
+ if (!empty($course->idnumber)) {
+ $oldidnumber = $course->idnumber;
+ $course->idnumber = tool_uploadcourse_increment_idnumber($course->idnumber);
+ if ($course->idnumber !== $oldidnumber) {
+ $upt->track('idnumber', s($oldidnumber).'-->'.s($course->idnumber), 'info');
+ }
+ }
+ $existingcourse = false;
+ }
+
+ // Check duplicate idnumber.
+ if (!$existingcourse and !empty($course->idnumber)) {
+ if ($DB->record_exists('course', array('idnumber' => $course->idnumber))) {
+ $upt->track('status', get_string('idnumbernotunique', 'tool_uploadcourse'), 'error');
+ $upt->track('idnumber', $errorstr, 'error');
+ $error = true;
+ }
+ }
+
+ // Notify about nay shortname changes.
+ if ($originalshortname !== $course->shortname) {
+ $upt->track('shortname', '', 'normal', false); // Clear previous.
+ $upt->track('shortname', s($originalshortname).'-->'.s($course->shortname), 'info');
+ } else {
+ $upt->track('shortname', s($course->shortname), 'normal', false);
+ }
+
+ // Add default values for remaining fields.
+ $formdefaults = array();
+ foreach ($std_fields as $field) {
+ if (isset($course->$field)) {
+ continue;
+ }
+ // All validation moved to form2.
+ if (isset($formdata->$field)) {
+ $course->$field = $formdata->$field;
+ $formdefaults[$field] = true;
+ if (in_array($field, $upt->columns)) {
+ $upt->track($field, s($course->$field), 'normal');
+ }
+ } else {
+ // Process templates.
+ if (isset($formdata->{"cc".$field}) && !empty($formdata->{"cc".$field}) && empty($course->$field)) {
+ $course->$field = tool_uploadcourse_process_template($formdata->{"cc".$field}, $course);
+ }
+ }
+ }
+ // Do we run the reset ?
+ $resetcourse = false;
+ if ($course->reset) {
+ $resetcourse = true;
+ unset($course->reset);
+ }
+
+ // Proof visible flag.
+ $course->visible = (int) $course->visible;
+
+ if (empty($course->category)) {
+ $course->category = $formdata->cccategory;
+ }
+
+ // Delete course.
+ if (!empty($course->deleted)) {
+ if (!$allowdeletes) {
+ $coursesskipped++;
+ $upt->track('status', $strcoursenotdeletedoff, 'warning');
+ continue;
+ }
+ if ($existingcourse) {
+ if (delete_course($existingcourse->id, false)) {
+ $upt->track('status', $strcoursedeleted);
+ $deletes++;
+ } else {
+ $upt->track('status', $strcoursenotdeletederror, 'error');
+ $deleteerrors++;
+ }
+ } else {
+ $upt->track('status', $strcoursenotdeletedmissing, 'error');
+ $deleteerrors++;
+ }
+ continue;
+ }
+ // We do not need the deleted flag anymore.
+ unset($course->deleted);
+
+ // Renaming requested?
+ if (!empty($course->oldshortname) ) {
+ if (!$allowrenames) {
+ $coursesskipped++;
+ $upt->track('status', $strcoursenotrenamedoff, 'warning');
+ continue;
+ }
+
+ if ($existingcourse) {
+ $upt->track('status', $strcoursenotrenamedexists, 'error');
+ $renameerrors++;
+ continue;
+ }
+
+ if ($standardshortnames) {
+ $oldshortname = clean_param($course->oldshortname, PARAM_MULTILANG);
+ } else {
+ $oldshortname = $course->oldshortname;
+ }
+
+ // No guessing when looking for old shortname, it must be exact match.
+ if ($oldcourse = $DB->get_record('course', array('shortname'=>$oldshortname))) {
+ $upt->track('id', $oldcourse->id, 'normal', false);
+ $DB->set_field('course', 'shortname', $course->shortname, array('id'=>$oldcourse->id));
+ $upt->track('shortname', '', 'normal', false); // Clear previous.
+ $upt->track('shortname', s($oldshortname).'-->'.s($course->shortname), 'info');
+ $upt->track('status', $strcourserenamed);
+ $renames++;
+ } else {
+ $upt->track('status', $strcoursenotrenamedmissing, 'error');
+ $renameerrors++;
+ continue;
+ }
+ $existingcourse = $oldcourse;
+ $existingcourse->shortname = $course->shortname;
+ }
+
+ // Can we process with update or insert?
+ $skip = false;
+ switch ($optype) {
+ case CC_COURSE_ADDNEW:
+ if ($existingcourse) {
+ $coursesskipped++;
+ $upt->track('status', $strcoursenotadded, 'warning');
+ $skip = true;
+ }
+ break;
+
+ case CC_COURSE_ADDINC:
+ if ($existingcourse) {
+ // This should not happen!
+ $upt->track('status', $strcoursenotaddederror, 'error');
+ $courseserrors++;
+ $skip = true;
+ }
+ break;
+
+ case CC_COURSE_ADD_UPDATE:
+ break;
+
+ case CC_COURSE_UPDATE:
+ if (!$existingcourse) {
+ $coursesskipped++;
+ $upt->track('status', $strcoursenotupdatednotexists, 'warning');
+ $skip = true;
+ }
+ break;
+
+ default:
+ // Unknown type.
+ $skip = true;
+ }
+
+ // Check for the backup file as template.
+ $backupfile = null;
+ if (!empty($course->backupfile)) {
+ if (!is_readable($course->backupfile) || !preg_match('/(\.mbz|\.zip)$/i', $course->backupfile)) {
+ $upt->track('status', get_string('incorrecttemplatefile', 'tool_uploadcourse'), 'error');
+ $courseserrors++;
+ $skip = true;
+ } else {
+ $backupfile = $course->backupfile;
+ }
+ }
+
+ if ($skip) {
+ continue;
+ }
+
+ // check the format
+ if (!empty($course->format) && !in_array($course->format, $courseformats)) {
+ $upt->track('status', get_string('incorrectformat', 'tool_uploadcourse'), 'error');
+ $courseserrors++;
+ continue;
+ }
+
+ $templatename = null;
+ if ($existingcourse) {
+ $course->id = $existingcourse->id;
+
+ $upt->track('shortname', html_writer::link(new moodle_url('/course/view.php',
+ array('id '=> $existingcourse->id)),
+ s($existingcourse->shortname)),
+ 'normal', false);
+
+ $existingcourse->timemodified = time();
+ // Do NOT mess with timecreated or firstaccess here!
+ $doupdate = false;
+
+ if ($updatetype != CC_UPDATE_NOCHANGES) {
+ foreach ($std_fields as $column) {
+ if ($column === 'shortname') {
+ // These can not be changed here.
+ continue;
+ }
+ if (!property_exists($course, $column) or !property_exists($existingcourse, $column)) {
+ // This should never happen.
+ continue;
+ }
+ // In the case $updatetype == CC_UPDATE_ALLOVERRIDE we override everything.
+ if ($updatetype == CC_UPDATE_MISSING) {
+ if (!is_null($existingcourse->$column) and $existingcourse->$column !== '') {
+ continue;
+ }
+
+ } else if ($updatetype == CC_UPDATE_FILEOVERRIDE) {
+ if (!empty($formdefaults[$column])) {
+ // Do not override with form defaults.
+ continue;
+ }
+ }
+ if ($existingcourse->$column !== $course->$column) {
+ if (in_array($column, $upt->columns)) {
+ $upt->track($column, s($existingcourse->$column).'-->'.s($course->$column), 'info', false);
+ }
+ $existingcourse->$column = $course->$column;
+ $doupdate = true;
+ }
+ }
+ }
+
+ if ($doupdate) {
+ // We want only courses that were really updated.
+ update_course($existingcourse);
+ $upt->track('status', $strcourseupdated);
+ $coursesupdated++;
+
+ events_trigger('course_updated', $existingcourse);
+
+ if ($bulk == CC_BULK_UPDATED or $bulk == CC_BULK_ALL) {
+ if (!in_array($course->id, $SESSION->bulk_courses)) {
+ $SESSION->bulk_courses[] = $course->id;
+ }
+ }
+
+ } else {
+ // No course information changed.
+ $upt->track('status', $strcourseuptodate);
+ $coursesuptodate++;
+
+ if ($bulk == CC_BULK_ALL) {
+ if (!in_array($course->id, $SESSION->bulk_courses)) {
+ $SESSION->bulk_courses[] = $course->id;
+ }
+ }
+ }
+
+ } else {
+ // Save the new course to the database.
+ $course->timemodified = time();
+ $course->timecreated = time();
+
+ // Create course - insert_record ignores any extra properties.
+ if (isset($course->templatename) && $course->templatename != 'none') {
+ $templatename = $course->templatename;
+ } else {
+ $templatename = null;
+ }
+ try {
+ $course = create_course($course);
+ } catch (moodle_exception $e) {
+ $upt->track('status', $e->getMessage(), 'error');
+ $courseserrors++;
+ $skip = true;
+ continue;
+ }
+ $upt->track('shortname', html_writer::link(new moodle_url('/course/view.php',
+ array('id' => $course->id)),
+ s($course->shortname)),
+ 'normal', false);
+
+ $upt->track('status', $strcourseadded);
+ $upt->track('id', $course->id, 'normal', false);
+ $coursesnew++;
+
+ // Make sure course context exists.
+ get_context_instance(CONTEXT_COURSE, $course->id);
+
+ events_trigger('course_created', $course);
+
+ if ($bulk == CC_BULK_NEW or $bulk == CC_BULK_ALL) {
+ if (!in_array($course->id, $SESSION->bulk_courses)) {
+ $SESSION->bulk_courses[] = $course->id;
+ }
+ }
+ }
+
+ // After creation/update, do we need to copy from template nominated in the CSV file?
+ if (!empty($templatename)) {
+ $coursetemplate = $DB->get_record('course', array('shortname' => $templatename));
+ if (empty($coursetemplate)) {
+ $upt->track('status', get_string('incorrecttemplatefile', 'tool_uploadcourse'), 'error');
+ $courseserrors++;
+ continue;
+ }
+
+ // Backup the course template.
+ $bc = new backup_controller(backup::TYPE_1COURSE, $coursetemplate->id, backup::FORMAT_MOODLE,
+ backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id);
+ $backupid = $bc->get_backupid();
+ $backupbasepath = $bc->get_plan()->get_basepath();
+ $bc->execute_plan();
+ $bc->destroy();
+ $packer = get_file_packer('application/zip');
+ // Check if tmp dir exists.
+ $tmpdir = $CFG->tempdir . '/backup';
+ if (!check_dir_exists($tmpdir, true, true)) {
+ throw new restore_controller_exception('cannot_create_backup_temp_dir');
+ }
+ $filename = restore_controller::get_tempdir_name(SITEID, $USER->id);
+ $temppathname = $tmpdir . '/' . $filename;
+ // Get the list of files in directory.
+ $filestemp = get_directory_list($backupbasepath, '', false, true, true);
+ $files = array();
+ foreach ($filestemp as $file) {
+ // Add zip paths and fs paths to all them.
+ $files[$file] = $backupbasepath . '/' . $file;
+ }
+ $zippacker = get_file_packer('application/zip');
+ $zippacker->archive_to_pathname($files, $temppathname);
+ if (empty($CFG->keeptempdirectoriesonbackup)) {
+ fulldelete($backupbasepath);
+ }
+
+ // Check if tmp dir exists.
+ $tmpdir = $CFG->tempdir . '/backup';
+ $filename = restore_controller::get_tempdir_name($course->id, $USER->id);
+ $pathname = $tmpdir . '/' . $filename;
+ $packer = get_file_packer('application/zip');
+ $packer->extract_to_pathname($temppathname, $pathname);
+
+ // Restore the backup immediately.
+ $rc = new restore_controller($filename, $course->id,
+ backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, backup::TARGET_CURRENT_ADDING);
+ // Check if the format conversion must happen first.
+ if ($rc->get_status() == backup::STATUS_REQUIRE_CONV) {
+ $rc->convert();
+ }
+ if (!$rc->execute_precheck()) {
+ $precheckresults = $rc->get_precheck_results();
+ if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
+ if (empty($CFG->keeptempdirectoriesonbackup)) {
+ fulldelete($pathname);
+ }
+ echo $output->precheck_notices($precheckresults);
+ if (!$plain) {
+ echo $output->continue_button(new moodle_url('/course/view.php', array('id' => $course->id)));
+ echo $output->footer();
+ }
+ die();
+ }
+ }
+ $rc->execute_plan();
+ $rc->destroy();
+ if (empty($CFG->keeptempdirectoriesonbackup)) {
+ fulldelete($pathname);
+ }
+ }
+
+ // After creation/update, do we need to copy from template?
+ if (!empty($templatepathname)) {
+ // Check if tmp dir exists.
+ $tmpdir = $CFG->tempdir . '/backup';
+ $filename = restore_controller::get_tempdir_name($course->id, $USER->id);
+ $pathname = $tmpdir . '/' . $filename;
+ $packer = get_file_packer('application/zip');
+ $packer->extract_to_pathname($templatepathname, $pathname);
+
+ // Restore the backup immediately.
+ $rc = new restore_controller($filename, $course->id,
+ backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, backup::TARGET_CURRENT_ADDING);
+ // Check if the format conversion must happen first.
+ if ($rc->get_status() == backup::STATUS_REQUIRE_CONV) {
+ $rc->convert();
+ }
+ if (!$rc->execute_precheck()) {
+ $precheckresults = $rc->get_precheck_results();
+ if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
+ if (empty($CFG->keeptempdirectoriesonbackup)) {
+ fulldelete($pathname);
+ }
+ echo $output->precheck_notices($precheckresults);
+ if (!$plain) {
+ echo $output->continue_button(new moodle_url('/course/view.php', array('id' => $course->id)));
+ echo $output->footer();
+ }
+ die();
+ }
+ }
+ $rc->execute_plan();
+ $rc->destroy();
+ if (empty($CFG->keeptempdirectoriesonbackup)) {
+ fulldelete($pathname);
+ }
+ }
+
+ // After creation/update, do we need to copy from template backup file?
+ if (!empty($restorefile)) {
+ // Check if tmp dir exists.
+ $tmpdir = $CFG->tempdir . '/backup';
+ $filename = restore_controller::get_tempdir_name($course->id, $USER->id);
+ $pathname = $tmpdir . '/' . $filename;
+ $packer = get_file_packer('application/zip');
+ $packer->extract_to_pathname($restorefile, $pathname);
+
+ // Restore the backup immediately.
+ $rc = new restore_controller($filename, $course->id,
+ backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, backup::TARGET_CURRENT_ADDING);
+ // Check if the format conversion must happen first.
+ if ($rc->get_status() == backup::STATUS_REQUIRE_CONV) {
+ $rc->convert();
+ }
+ if (!$rc->execute_precheck()) {
+ $precheckresults = $rc->get_precheck_results();
+ if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
+ if (empty($CFG->keeptempdirectoriesonbackup)) {
+ fulldelete($pathname);
+ }
+ echo $output->precheck_notices($precheckresults);
+ if (!$plain) {
+ echo $output->continue_button(new moodle_url('/course/view.php', array('id' => $course->id)));
+ echo $output->footer();
+ }
+ die();
+ }
+ }
+ $rc->execute_plan();
+ $rc->destroy();
+ if (empty($CFG->keeptempdirectoriesonbackup)) {
+ fulldelete($pathname);
+ }
+ }
+
+ // After creation/update, do we need to import a Moodle backup?
+ if (!empty($backupfile)) {
+ // Check if tmp dir exists.
+ $tmpdir = $CFG->tempdir . '/backup';
+ if (!check_dir_exists($tmpdir, true, true)) {
+ throw new restore_controller_exception('cannot_create_backup_temp_dir');
+ }
+ $filename = restore_controller::get_tempdir_name($course->id, $USER->id);
+ $pathname = $tmpdir . '/' . $filename;
+ $packer = get_file_packer('application/zip');
+ $packer->extract_to_pathname($backupfile, $pathname);
+
+ // Restore the backup immediately.
+ $rc = new restore_controller($filename, $course->id,
+ backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, backup::TARGET_CURRENT_ADDING);
+ // Check if the format conversion must happen first.
+ if ($rc->get_status() == backup::STATUS_REQUIRE_CONV) {
+ $rc->convert();
+ }
+ if (!$rc->execute_precheck()) {
+ $precheckresults = $rc->get_precheck_results();
+ if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
+ if (empty($CFG->keeptempdirectoriesonbackup)) {
+ fulldelete($pathname);
+ }
+ echo $output->precheck_notices($precheckresults);
+ if (!$plain) {
+ echo $output->continue_button(new moodle_url('/course/view.php', array('id' => $course->id)));
+ echo $output->footer();
+ }
+ die();
+ }
+ }
+ $rc->execute_plan();
+ $rc->destroy();
+ if (empty($CFG->keeptempdirectoriesonbackup)) {
+ fulldelete($pathname);
+ }
+ }
+
+ // Handle enrolment methods.
+ $enrol_updated = false;
+ $instances = enrol_get_instances($course->id, false);
+ foreach ($enrolments as $method) {
+ if (isset($method['delete']) && $method['delete']) {
+ // Remove the enrolment method.
+ foreach ($instances as $instance) {
+ if ($instance->enrol == $method['enrolmethod']) {
+ $plugin = $enrolmentplugins[$instance->enrol];
+ $plugin->delete_instance($instance);
+ $enrol_updated = true;
+ break;
+ }
+ }
+ } else if (isset($method['disable']) && $method['disable']) {
+ // Disable the enrolment.
+ foreach ($instances as $instance) {
+ if ($instance->enrol == $method['enrolmethod']) {
+ $plugin = $enrolmentplugins[$instance->enrol];
+ $plugin->update_status($instance, ENROL_INSTANCE_DISABLED);
+ $enrol_updated = true;
+ break;
+ }
+ }
+ } else {
+ // We should have this enrolment method.
+ $instance = null;
+ foreach ($instances as $i) {
+ if ($i->enrol == $method['enrolmethod']) {
+ $instance = $i;
+ break;
+ }
+ }
+ $plugin = null;
+ if (empty($instance)) {
+ $plugin = $enrolmentplugins[$method['enrolmethod']];
+ $instance = new stdClass();
+ $instance->id = $plugin->add_default_instance($course);
+ $instance->roleid = $plugin->get_config('roleid');
+ } else {
+ $plugin = $enrolmentplugins[$instance->enrol];
+ $plugin->update_status($instance, ENROL_INSTANCE_ENABLED);
+ }
+ // Now update values.
+ foreach ($method as $k => $v) {
+ $instance->{$k} = $v;
+ }
+
+ // Sort out the start, end and date.
+ $instance->enrolstartdate = (isset($method['startdate']) ? strtotime($method['startdate']) : 0);
+ $instance->enrolenddate = (isset($method['enddate']) ? strtotime($method['enddate']) : 0);