You are on page 1of 21

<?

php /// Bulk course creation script from a comma separated file // For speed reasons, bypasses ADODB so only works with MySQL // - Went from taking about half an hour to under a minute // // // // // Additionally for speed reasons, INSERT queries are concatenated, and can get quite large: if you have checked everything, and errors are occuring on large inserts, try upping the MySQL Server query limit (a function of "net buffer length", though more my.cnf/my.ini tweaking could be required)

// Somewhat hacked from /admin/uploaduser.php // @Moodle: fgetcsv has been here since PHP3, it's much faster than any Perl-sty le explode tricks! // Update: Minor changes to validation (seems faster, more extensible) and added // appropriate SQL maintenance queries - quite a lot of database activity could // cause problems, especially under MySQL. // Update: Ability to specify slash-delimited "Category paths", // and will also create new categories as necessary. // N.B. If a category name is not unique, will pick the first one it comes acros s. // Update: Can specify section headings (weeks/topics). // Update: Can enrol course teachers and set associated information // Ability to specify user account via ID or a unique (for security rea sons) search string // (as used on the Administration > Edit User Accounts page) // N.B. Using user account search strings instead of IDs gives a performance hit with a large number of teachers // I did not add Student enrolment, though this would be relatively trivial, as this is already // handled by the far more elegant enrolment plugin system. // Teachers are a sort of "course property", but Students are not. // Rory Allford (rory@allford.net)

// ---------------------------------------------------------------------------------------------------// UPDATED BY: Ashley Gooding & Cole Spicer // UPDATE: Added compatability for newer versions of moodle as well as made futu re updates easier // Just add new mdl_course column names to $optional and $validat e arrays. // DATE: 3/27/07 // @version v1.1

// ---------------------------------------------------------------------------------------------------// ---------------------------------------------------------------------------------------------------// // se // // UPDATED BY: Ashley Gooding & Cole Spicer UPDATE: Add template functionality. Specify the shortname of an existing cour to serve as a template. DATE: 5/9/07 @version v1.2

// ---------------------------------------------------------------------------------------------------// UPDATED BY: Jason Hollowell // UPDATE: Messy hack to enable the script to create courses under 2.0. Creation of several fields (listed below) was disabled because they have been // moved to a different table (or removed) in 2.0. One important elimination is the ability to enroll teachers at the time of course creation. The next // version of this script will hopefully have that capability reimplemented. // DATE: 2/23/2011 // @version v1.3.1 //-----------------------------------------------------------------------------------------------------// UPDATED BY Kathryn Duncan 6/3/2011 // UPDATE: When a course is successfully added, it adds a record to the enrol t able, to allow manual enrolments // to the course. The default role for these is student. // DATE: 6 March 2011 // @version v1.3.2 //------------------------------------------------------------------------------------------------------//error_reporting(E_ALL // Some Regex constants E_STRICT );

//YUSH HACK: need to connect manually first mysql_connect("localhost","user","password"); mysql_select_db("dbname"); //Ideally this should be avoided define('TOPIC_FIELD','/^(topic)([0-9] [1-4][0-9] 5[0-2])$/'); define('TEACHER_FIELD','/^(teacher)([1-9]+\d*)(_account _role)$/'); require_once($CFG->dirroot . '../config.php'); require_once($CFG->dirroot . '/course/lib.php'); require_once($CFG->libdir.'/adminlib.php'); // JLH added require_once($CFG->libdir . '/enrollib.php'); require_once("$CFG->libdir/blocklib.php"); $courseid=-1; $systemcontext = get_context_instance(CONTEXT_SYSTEM); // JLH

$PAGE->set_url('/admin/uploadcourse.php'); //added by JLH $PAGE->set_context($systemcontext); // JLH $PAGE->set_pagelayout('admin'); // Set a default pagelayout (also added by J LH) function csverror($message, $link='') { global $CFG, $SESSION, $OUTPUT; //JLH added $OUTPUT variable here //echo $OUTPUT->header(); // JLH - commented this line out and got past the STATE error...? Also changed from print_header to $OUTPUT echo '<br />'; $message = clean_text($message); //print_simple_box('<span style="font-family:monospace;color:#000000;">'.$m essage.'</span>', 'center', '', '#FFBBBB', 5, 'errorbox'); From 1.9 version echo $OUTPUT->box_start(); echo $OUTPUT->notification('<span style="font-family:monospace;color:#000 000;">'.$message.'</span>', 'center', '', '#FFBBBB', 5, 'errorbox'); echo $OUTPUT->box_end(); if (!$link) { if ( !empty($SESSION->fromurl) ) { $link = $SESSION->fromurl; unset($SESSION->fromurl); } else { $link = $CFG->wwwroot .'/'; } } print_continue($link); echo $OUTPUT->footer(); die; } function getdefaultcategory() { static $mysqlresource1; static $cat; global $CFG; global $USER; ($mysqlresource1=mysql_query('SELECT MIN(`id`) FROM `'.$CFG->prefix.'cou rse_categories`')) or csverror('You have no category table!','uploadcourse.php?s esskey='.$USER->sesskey); if (mysql_num_rows($mysqlresource1)) { $cat = mysql_fetch_row($mysqlresource1); return $cat[0]; } else { return 1; // *SHOULD* be the Misc category? } } function checkisint($supposedint) { return ((string)intval($supposedint) == $supposedint) ? true : false; } function checkisstring($supposedstring) { $supposedstring = trim($supposedstring); // Is it just spaces? return (strlen($supposedstring) == 0) ? false : true; } function validateas($value, $validatename, $lineno, $fieldname = '') {

// Validates each field based on information in the $validate array global $USER; global $validate; ($fieldname=='') and ($fieldname=$validatename); (isset($validate[$validatename])) or csverror('Coding Error: Unvalidated field type: "'.$validatename.'"','uploadcourse.php?sesskey='.$USER->sesskey); $format = $validate[$validatename]; switch($format[0]) { case 1: // String if (($maxlen = $format[1])!=0) // Max length? (strlen($value) <=$format[1]) or csverror('Invalid value for field '.$fieldname.' (length &gt; '.$format[1].'). '. get_string('erroronline', 'error', $lineno ) .". ". get_string('processingstops', 'error'), 'uploadcourse.php?sesskey='.$USER->sesskey ); if ($format[2] == 1) // Not null? checkisstring($value) or csverror('Invalid value for field ' .$fieldname.' (only spaces or missing). '. get_string('erroronline', 'error', $lineno) ." . ". get_string('processingstops', 'error'), 'uploadcourse.php?sesskey='.$USER->sesskey); break; case 2: // Integer checkisint($value) or csverror('Invalid value for field '.$field name.' (not an integer). '. get_string('erroronline', 'error', $lineno) ." . ". get_string('processingstops', 'error'), 'uploadcourse.php?sesskey='.$USER->sesskey); if (($max = $format[1])!=0) // Max value? ($value <= $max) or csverror('Invalid value for field '.$fie ldname.' (&gt; '.$max.'). '. get_string('erroronline', 'error', $lineno) .". ". get_string('processingstops', 'error'), 'uploadcourse.php?sesskey='.$USER->sesskey); $min = $format[2]; // Min value ($value >= $min) or csverror('Invalid value for field '.$fie ldname.' (&lt; '.$min.'). '. get_string('erroronline', 'error', $lineno) .". ". get_string('processingstops', 'error'), 'uploadcourse.php?sesskey='.$USER->sesskey); break; case 3: // Timestamp - validates and converts to Unix Time if (($value = strtotime($value)) < 1) csverror('Invalid value for field '.$fieldname.' (Bad Timestamp). '. get_string('erroronline', 'error', $lineno) ." . ".

get_string('processingstops', 'error'), 'uploadcourse.php?sesskey='.$USER->sesskey); break; case 4: // Domain $validvalues = explode(',',$format[1]); if (array_search($value,$validvalues)===false) csverror('Invalid value for field '.$fieldname.' (Must be on e of {'.$format[1].'}). '. get_string('erroronline', 'error', $lineno) .". ". get_string('processingstops', 'error'), 'uploadcourse.php?sesskey='.$USER->sesskey); break; case 5: // Category if (checkisint($value)) { // It's a Category ID Number categoryexists_ex($value) or csverror('Invalid value for field ' .$fieldname.' (No Category with ID '.$value.'). '. get_string('erroronline', 'error', $lineno) .". ". get_string('processingstops', 'error'), 'uploadcourse.php?sesskey='.$USER->sesskey); } elseif(checkisstring($value)) { // It's a Category Path string $value=trim(str_replace('\\','/',$value)," \t\n\r\0\x0B/"); // Clean path, ensuring all slashes are forward ones (strlen($value)>0) or csverror('Invalid value for field '.$fieldn ame.' (Path string not set). '. get_string('erroronline', 'error', $lineno) .". ". get_string('processingstops', 'error'), 'uploadcourse.php?sesskey='.$USER->sesskey); unset ($cats); $cats=explode('/',$value); // Break up path into array (count($cats)>0) or csverror('Invalid value for field '.$fieldna me.' (Path string "'.$value.'" invalid - not delimited correctly). '. get_string('erroronline', 'error', $lineno) .". ". get_string('processingstops', 'error'), 'uploadcourse.php?sesskey='.$USER->sesskey); foreach ($cats as $n => $item) { // Validate the path $item=trim($item); // Remove whitespace (strlen($item) <= 30) or csverror('Invalid value for field '.$ fieldname.' (Category name "'.$item.'" length &gt; 30). '. get_string('erroronline', 'error', $lineno ) .". ". get_string('processingstops', 'error'), 'uploadcourse.php?sesskey='.$USER->sesskey ); checkisstring($item) or csverror('Invalid value for field '.$f ieldname.' (Path string "'.$value.'" invalid - category name at position '.($n+1 ).' as shown is invalid). '. get_string('erroronline', 'error', $lineno) .". ". get_string('processingstops', 'error'),

'uploadcourse.php?sesskey='.$USER->sesskey); } $value=$cats; // Return the array unset ($cats); } else { csverror('Invalid value for field '.$fieldname.' (not an integer or string). '. get_string('erroronline', 'error', $lineno) .". ". get_string('processingstops', 'error'), 'uploadcourse.php?sesskey='.$USER->sesskey); } break; case 6: // User ID or Name (Search String) $value=trim($value); if (checkisint($value)) { // User ID userexists_ex($value) or csverror('Invalid value for field '.$f ieldname.' (No User with ID '.$value.'). '. get_string('erroronline', 'error', $lineno) .". ". get_string('processingstops', 'error'), 'uploadcourse.php?sesskey='.$USER->sesskey); } elseif (checkisstring($value)) { // User Search String // Only PHP5 supports named arguments $usersearch=get_users_listing('lastaccess','ASC',0,99999,mysql_r eal_escape_string($value),'',''); if (isset($usersearch) and ($usersearch!==false) and is_array($u sersearch) and (($ucount=count($usersearch))>0)) { ($ucount==1) or csverror('Invalid value for field '.$fieldna me.' (Search string ambiguous; returned multiple ['.$ucount.'] results). '. get_string('erroronline', 'error', $lineno) ." . ". get_string('processingstops', 'error'), 'uploadcourse.php?sesskey='.$USER->sesskey); reset($usersearch); $uid=key($usersearch); (checkisint($uid) && userexists_ex($uid)) or csverror('Inval id value for field '.$fieldname.' (Search string returned a nonexistent user ?!) . '. get_string('erroronline', 'error', $lineno) ." . ". get_string('processingstops', 'error'), 'uploadcourse.php?sesskey='.$USER->sesskey); $value=$uid; // Return found user id } else { csverror('Invalid value for field '.$fieldname.' (Search str ing returned no results). '. get_string('erroronline', 'error', $lineno) ." . ". get_string('processingstops', 'error'), 'uploadcourse.php?sesskey='.$USER->sesskey); } } else { if ($format[1] == 1) // Not null? (changed $format[2] == 1 to [1] due to an undefined offset error JLH csverror('Invalid value for field '.$fieldname.' (only space s or missing). '. get_string('erroronline', 'error', $lineno) ." . ". get_string('processingstops', 'error'),

'uploadcourse.php?sesskey='.$USER->sesskey); } break; default: csverror('Coding Error: Bad field validation type: "'.$fieldname.'"' ,'uploadcourse.php?sesskey='.$USER->sesskey); break; } return $value; } function categoryexists_ex($categoryid) { // Does category with given id exist? global $CFG; static $mysqlresource1; if ($mysqlresource1=mysql_query('SELECT `id` FROM `'.$CFG->prefix.'cours e_categories` WHERE `id`='.mysql_real_escape_string($categoryid))){ return mysql_num_rows($mysqlresource1) ? true : false; } else { return false; } } function userexists_ex($userid) { // Does category with given id exist? global $CFG; static $mysqlresource1; if ($mysqlresource1=mysql_query('SELECT `id` FROM `'.$CFG->prefix.'user` WHERE `deleted` = 0 AND `id`='.mysql_real_escape_string($userid))){ return mysql_num_rows($mysqlresource1) ? true : false; } else { return false; } } function microtime_float() { // In case we don't have php5 list($usec, $sec) = explode(" ", microtime()); return ((float)$usec + (float)$sec); } function mystr($value) { // Prepare $value for inclusion in SQL Query return '\''.mysql_real_escape_string($value).'\'';; } function fast_get_category_ex($hname, &$hstatus, $hparent=0) { // Find category with the given name and parentID, or create it, in both cases returning a category ID /* $hstatus: -1 : Failed to create category 1 : Existing category found 2 : Created new category successfully */ static $mysqlresource1; static $mysqlresource2; static $cat;

static $cname; global $CFG; global $USER; $cname = mystr($hname); // Check if a category with the same name and parent ID already exists $mysqlresource1=mysql_query('SELECT `id` FROM `'.$CFG->prefix.'course_ca tegories` WHERE `name` = '.$cname.' AND `parent` = '.$hparent); if (mysql_num_rows($mysqlresource1)) { $cat = mysql_fetch_row($mysqlresource1); $hstatus=1; return $cat[0]; } else { // Create it - moodle does set sortorder to 999, and doesn't use the description field if (!($mysqlresource2=mysql_query('INSERT INTO `'.$CFG->prefix.' course_categories` (`name`,`description`,`parent`,`sortorder`,`coursecount`,`vis ible`,`timemodified`) VALUES ('.$cname.',\'\','.$hparent.',999,0,1,0);'))) { // Failed! $hstatus=-1; return -1; } else { $hstatus=2; return mysql_insert_id(); } } } function fast_is_course($hshortname) { // Does a course with the given shortname exist? global $CFG; static $mysqlresource1; static $cshortname; $cshortname = mystr($hshortname); if ($mysqlresource1=mysql_query('SELECT `id` FROM `'.$CFG->prefix.'cours e` WHERE `ShortName`='.$cshortname)) { // Check shortname is unique before inser ting if (mysql_num_rows($mysqlresource1)) { return true; } else { return false; } } else { return false; } } // Edited by Ashley Gooding & Cole Spicer to fix problems with 1.7.1 and make easier to dynamically add new columns function fastcreatecourse_ex($hcategory, $course, $header, $validate) { if(!is_array($course) !is_array($header) !is_array($valida te)) { return -2;

} global $CFG; // declaring as static prevents object pointers being continually create d and destroyed, saving time in theory static $courseid; static $mysqlresource1; static $mysqlresource2; static $mysqlresource3; static $mysqlresource4; static $mysqlresource5; //added for second insert JLH static $dcomma; // Creating SQL for composite fields static $dtopics; static $dtopicno; static $dtopicname; static $dteachers; static $dteacherno; static $dteacherdata; // Dynamically Create Query Based on number of headings excludin g Teacher[1,2,...] and Topic[1,2,...] // Added for increased functionality with newer versions of moodle // Author: Ashley Gooding & Cole Spicer static $tempstring; $query = 'INSERT INTO `'.$CFG->prefix.'course` (`category`,'; foreach ($header as $i => $col) { $col = strtolower($col); if(preg_match(TOPIC_FIELD, $col) preg_match(TEACHER_F $col == 'category' $col == 'template') { continue; } $query = $query.'`'.$col.'`,'; } $query = $query.'`modinfo`) VALUES ('.$hcategory.','; foreach ($header as $i => $col) { $col = strtolower($col); if(preg_match(TOPIC_FIELD, $col) preg_match(TEACHER_F IELD, $col) $col == 'category' $col == 'template') { continue; } if($col == 'expirythreshold') { $course[$col] = $course[$col]*86400; } // print_r($validate[$col]); // JLH added to check conte nts of the array $temparray = explode(',',$validate[$col][1]); if($validate[$col][0] == 1 ($validate[$col][0] == 4 && !checkisint($temparray[0]) ) ) { //String or Domain with strings $query = $query.''.mystr($course[$col]).', '; } else { $query = $query.''.$course[$col].', '; } } $query = $query.' \'\');'; // End Dynamic Query

IELD, $col)

if (!($mysqlresource2=mysql_query($query))) { return -2; }

$courseid=mysql_insert_id(); if(isset($course['template']) && $course['template'] != '') { if(!($mysqlresource6 = mysql_query('SELECT `id` FROM `'.$CFG->prefix.'course` WHERE `shortname`=\''.$course['template'].'\';')) ) { return -7; } if(!mysql_num_rows($mysqlresource6)) { return -7; } $row = mysql_fetch_row($mysqlresource6); $id = $row[0]; if(! ($mysqlresource7 = mysql_query(' INSERT INT O `'.$CFG->prefix.'block_instance` ( `blockid` , `pageid` , `pagetype` , `positi on` , `weight` , `visible` , `configdata` ) SELECT `blockid` , '.$courseid.', `p agetype` , `position` , `weight` , `visible` , `configdata` FROM `'.$CFG->prefix .'block_instance` WHERE `pageid` = '.$id.';') )) { return -8; } } else { //$page = page_create_object(PAGE_COURSE_VIEW, $courseid); //blocks_repopulate_page($page); // Setup blocks } $dtopics=''; // String concatenation for topic INSERT $dcomma=false; // Should we add a comma before the next item? if (isset($course['topics'])) { // Any topic headings specified ? foreach($course['topics'] as $dtopicno => $dtopicname) if ($dtopicno <= $course['numsections']) { // Avoid overflowing topi c headings if ($dcomma==true) { $dtopics.=','; } else { $dcomma=true; } $dtopics.='('.$courseid.','.mystr($dtopicno).','.mystr($dtopicna me).',\'\',\'1\')'; } } if (!isset($course['topics'][0])) { // Ensure at least default topic section exists if ($dcomma==true) { $dtopics.=','; } else { $dcomma=true; } $dtopics.='(\''.$courseid.'\',\'0\',\'\',\'\',\'1\');'; } else {

$dtopics.=';'; } if (!($mysqlresource3=mysql_query('INSERT INTO `'.$CFG->prefix.'course_s ections` (`course`,`section`,`summary`,`sequence`,`visible`) VALUES '.$dtopics)) ) return -3; $dteachers=''; // String concatenation for topic INSERT $dcomma=false; // Should we add a comma before the next item?

// SELECT id FROM mdl_role WHERE name = blah // use that id and change insert to: // mdl_role_assignments // roleid contextid userid modifierid enrol // courseshit 0 'manual'

timemodified

// If SELECT id... doesnt work (returns false or contians no it ems) then throw error $roleid; if (!$context = get_context_instance(CONTEXT_COURSE, $courseid)) { return -6; } $coursecontext = get_context_instance(CONTEXT_COURSE, $courseid) ; // JLH //print_r($coursecontext); // JLH // if '])>0)) { // // { // // // // // // // CT `id` FROM .'\';')) ) { // // // // // // or 2.0 //YUSH HACK: you need to lose the ; at t he end for multiple value sets // $dteachers.='('.$row[0].','.$context->id.','.$dteacherdata ['_account'].','.$course['timecreated'].',0);'; (isset($course['teachers_enrol']) && (count($course['teachers_enrol Any teachers specified? foreach($course['teachers_enrol'] as $dteacherno => $dteacherdata) if (isset($dteacherdata['_account'])) { if ($dcomma==true) { $dteachers.=','; } else { $dcomma=true; } if(!($mysqlresource5 = mysql_query('SELE `'.$CFG->prefix.'role` WHERE `shortname`=\''.$dteacherdata['_role'] return -5; } if(!mysql_num_rows($mysqlresource5)) { return -5; } $row = mysql_fetch_row($mysqlresource5); //YUSH HACK: the 'enrol' field is gone f

// //

} }

// if ($dteachers!='') { // $dteachers .= ";"; // // if (!($mysqlresource4=mysql_query('INSERT INTO `'.$CFG->prefix .'role_assignments` (`roleid`,`contextid`,`userid`,`timemodified`,`modifierid`) VALUES '.$dteachers))) // from original // // // return -4; // } // } return 1; } require_login(); //YUSH HACK: isadmin() is gone from 2.0 // if (!isadmin()) { // csverror('You must be an administrator to edit courses in this way.'); // } //try this if (!is_siteadmin()) { csverror('You must be an administrator to edit courses in this w ay.'); } // // // if (!confirm_sesskey()) { csverror(get_string('confirmsesskeybad', 'error')); } if (! $site = get_site()) { csverror('Could not find site-level course'); } if (!$adminuser = get_admin()) { csverror('Could not find site admin'); } set_time_limit(300); // Up the php timeout $time_start = microtime_float(); // Just for timing $stradministration = get_string('administration'); $strchoose = get_string('choose'); $struploadcourses = 'Upload Courses'; $csv_encode = '/\&\#44/'; if (isset($CFG->CSV_DELIMITER)) { $csv_delimiter = '\\' . $CFG->CSV_DELIMITER; $csv_delimiter2 = $CFG->CSV_DELIMITER; if (isset($CFG->CSV_ENCODE)) { $csv_encode = '/\&\#' . $CFG->CSV_ENCODE . '/';

} } else { $csv_delimiter = '\,'; $csv_delimiter2 = ','; } /// Print the header echo $OUTPUT->header("$site->shortname: $struploadcourses", $site->fullname, "<a href=\"index.php\">$stradministration</a> -> $struploadcour ses"); /// If a file has been uploaded, then process it require_once($CFG->dirroot.'/lib/uploadlib.php'); $um = new upload_manager('coursefile',false,false,null,false,0); if ($um->preprocess_files()) { if (!isset($um->files['coursefile'])) csverror('Upload Error!', 'uploadcourse.php?sesskey='.$USER->sesskey ); $filename = $um->files['coursefile']['tmp_name']; // Everything to Unix Line Endings $text = my_file_get_contents($filename); $text = preg_replace('!\r\n?!',"\n",$text); if ($fp = fopen($filename, "w")) { fwrite($fp,$text); unset($text); // Memory! fclose($fp); } else { csverror('File I/O Error! (1)', 'uploadcourse.php?sesskey='.$USER->s esskey); } if (!$fp = fopen($filename, "r")) { csverror('File I/O Error! (2)', 'uploadcourse.php?sesskey='.$USER->s esskey); } // make arrays of fields for error checking $defaultcategory = getdefaultcategory(); $defaultmtime = time(); $required = array( 'fullname' => false, // Mandatory fields 'shortname' => false); $optional = array( 'category' => $defaultcategory, // Default values fo r optional fields 'sortorder' => 0, 'summary' => 'Write a concise and interesting paragr aph here that explains what this course is about', 'format' => 'weeks', 'showgrades' => 1, 'newsitems' => 5,

'teacher' => 'Teacher', 'teachers' => 'Teachers', 'student' => 'Student', 'students' => 'Students', 'startdate' => $defaultmtime, 'numsections' => 10, 'maxbytes' => 2097152, 'visible' => 1, 'groupmode' => 0, 'timecreated' => $defaultmtime, 'timemodified' => $defaultmtime, 'idnumber' => '', 'password' => '', 'enrolperiod' => 0, 'groupmodeforce' => 0, 'metacourse' => 0, 'lang' => '', 'theme' => '', 'cost' => '', 'showreports' => 0, 'guest' => 0, 'enrollable' => 1, 'enrolstartdate' => $def aultmtime, 'enrolenddate' => $defau ltmtime, 'notifystudents' => 0, 'template' => '', 'expirynotify' => 0, 'expirythreshold' => 10) ; $validate = array( 'fullname' => array(1,254,1), // Validation informat ion - see validateas function 'shortname' => array(1,15,1), 'category' => array(5), 'sortorder' => array(2,4294967295,0), 'summary' => array(1,0,0), 'format' => array(4,'social,topics,weeks'), 'showgrades' => array(4,'0,1'), 'newsitems' => array(2,10,0), 'teacher' => array(1,100,1), 'teachers' => array(1,100,1), 'student' => array(1,100,1), 'students' => array(1,100,1), 'startdate' => array(3), 'numsections' => array(2,52,0), 'maxbytes' => array(2,$CFG->maxbytes,0), 'visible' => array(4,'0,1'), 'groupmode' => array(4,NOGROUPS.','.SEPARATEGROUPS.' ,'.VISIBLEGROUPS), 'timecreated' => array(3), 'timemodified' => array(3), 'idnumber' => array(1,100,0), 'password' => array(1,50,0), 'enrolperiod' => array(2,4294967295,0), 'groupmodeforce' => array(4,'0,1'), 'metacourse' => array(4,'0,1'), 'lang' => array(1,50,0), 'theme' => array(1,50,0),

'cost' => array(1,10,0), 'showreports' => array(4,'0,1'), 'guest' => array(4,'0,1,2'), 'enrollable' => array(4, '0,1'), 'enrolstartdate' => arra y(3), 'enrolenddate' => array( 3), 'notifystudents' => arra y(4,'0,1'), 'template' => array(1,0, 0), 'expirynotify' => array( 4,'0,1'), 'expirythreshold' => arr ay(2,30,1), // Following ones cater for [something]N 'topic' => array(1,0,0), 'teacher_account' => array(6,0), 'teacher_role' => array(1,40,0)); $header = fgetcsv($fp, 1024); // check for valid field names if (($header[0]==null)&&(count($line)<1)) // Blank Line? csverror('First line must be the CSV header', 'uploadcourse.php?sess key='.$USER->sesskey); foreach ($header as $i => $h) { if ($h==null) csverror('Null CSV columns are not permitted in header', 'upload course.php?sesskey='.$USER->sesskey); if (preg_match(TOPIC_FIELD,$h)) { // Regex defined heade r names } elseif (preg_match(TEACHER_FIELD,$h)) { } else { if (!(isset($required[$h]) isset($optional[$h]))) csverror(get_string('invalidfieldname', 'error', $h), 'uploa dcourse.php?sesskey='.$USER->sesskey); if (isset($required[$h])) $required[$h] = true; // Mark Field as present } } // check for required fields foreach ($required as $key => $value) { if ($value != true) csverror(get_string('fieldrequired', 'error', $key), 'uploadcour se.php?sesskey='.$USER->sesskey); } $fieldcount = count($header); $lineno=2; unset($bulkcourses); while (($line = fgetcsv($fp, 1024)) !== false) { if (($line[0]!=null) (count($line)>1)) { // Blank Line? if (count($line) > $fieldcount) csverror('Too many actual values. '.

get_string('erroronline', 'error', $lineno) .". ". get_string('processingstops', 'error'), 'uploadcourse.php?sesskey='.$USER->sesskey); foreach ($header as $i => $h) { // Is line complete? if (!isset($line[$i])) { csverror(get_string('missingfield', 'error', $h). ". ". get_string('erroronline', 'error', $lineno) .". ". get_string('processingstops', 'error'), 'uploadcourse.php?sesskey='.$USER->sesskey); } } echo "<hr>Line ok:". implode(",",$line); unset($coursetocreate); unset($coursetopics); unset($courseteachers); foreach ($optional as $key => $value) { // Set course array to d efaults $coursetocreate[$key] = $value; } foreach ($line as $key => $value) { // Validate each value $cf = $header[$key]; if (preg_match(TOPIC_FIELD,$cf,$matches)) { $coursetopics[$matches[2]] = validateas($value,$matches[1] , $lineno, $cf); } elseif (preg_match(TEACHER_FIELD,$cf,$matches)) { $tmp=validateas($value,$matches[1].$matches[3], $lineno, $ cf); (isset($tmp)&&($tmp!='')) and ($courseteachers[$matches[2]][$matches[3]] = $tmp); } else { $coursetocreate[$cf] = validateas($value, $cf, $lineno); // Accept value if it passed validation } } // $coursetocreate['topics'] = $coursetopics; JLH - Commented out as this does not exist in the course table for 2.0 if (isset($courseteachers)) foreach ($courseteachers as $key => $value) // Deep validate cou rse teacher info on second pass { //YUSH HACK: added to skip blank account if (isset($value['_account']) ) if (isset($value) && (count($value) > 0)){ if (!(isset($value['_account'])&&checkisint($value['_account ']))) csverror('Invalid value for field teacher'.$key.' - oth er fields were specified but required teacher'.$key.'_account was null. '. get_string('erroronline', 'error', $lineno) .". ". get_string('processingstops', 'error'), 'uploadcourse.php?sesskey='.$USER->sesskey); // Hardcoded default values (that are as close to moodle's U I as possible) // and we can't assume PHP5 so no pointers! //if (!isset($value['_role'])) JLH - Commented out as this h as been moved to the users table in 2.0 // $courseteachers[$key]['_role'] = ''; JLH - Commented o ut as this has been moved to the users table in 2.0

} } $coursetocreate['teachers_enrol'] = $courseteachers; $bulkcourses[] = $coursetocreate; // Merge into array } $lineno++; } fclose($fp); if ((!isset($bulkcourses)) or (count($bulkcourses)==0)) csverror('No courses were parsed from CSV', 'uploadcourse.php?sesske y='.$USER->sesskey); else notify('Parsed '.count($bulkcourses).' course(s) from CSV','notifysu ccess'); // Running Status Totals $t $s $n $p = = = = 0; 0; 0; 0; // // // // Read courses Skipped courses Created courses Broken courses (failed halfway through

$cat_e = 0; // Errored categories $cat_c = 0; // Created categories foreach ($bulkcourses as $bulkcourse) { // Try to create the course if (!fast_is_course($bulkcourse['shortname'])) { $coursetocategory = 0; // Category ID if (is_array($bulkcourse['category'])) { // Course Category creation routine as a category path was g iven $curparent=0; $curstatus=0; foreach ($bulkcourse['category'] as $catindex => $catname) { $curparent = fast_get_category_ex($catname,$curstatus,$cur parent); switch ($curstatus) { case 1: // Skipped the category, already exists break; case 2: // Created a category $cat_c++; break; default: $cat_e+=count($bulkcourse['category']) - $catindex; $coursetocategory=-1; notify('An error occured creating category with name '.$catname.', meaning a total '.(count($bulkcourse['category']) - $catindex).' category(ies) failed','notifyproblem'); break 2; } } ($coursetocategory==-1) or $coursetocategory = $curparent; // Last category created will contain the actual course

} else { // It's just a straight category ID $coursetocategory=$bulkcourse['category']; } if ($coursetocategory==-1) { notify('Course with ShortName '.$bulkcourse['shortname'].' c ould not be created because parent category(ies) failed','notifyproblem'); } else { //switch (fastcreatecourse_ex($coursetocategory, $bulkcourse ['sortorder'], $bulkcourse['fullname'], $bulkcourse['shortname'], $bulkcourse['s ummary'], $bulkcourse['format'], $bulkcourse['showgrades'], $bulkcourse['newsite ms'], $bulkcourse['teacher'], $bulkcourse['teachers'], $bulkcourse['student'], $ bulkcourse['students'], $bulkcourse['startdate'], $bulkcourse['numsections'], $b ulkcourse['maxbytes'], $bulkcourse['visible'], $bulkcourse['groupmode'], $bulkco urse['timecreated'], $bulkcourse['timemodified'], $bulkcourse['idnumber'], $bulk course['password'], $bulkcourse['enrolperiod'], $bulkcourse['groupmodeforce'], $ bulkcourse['metacourse'], $bulkcourse['lang'], $bulkcourse['theme'], $bulkcourse ['cost'], $bulkcourse['showreports'], $bulkcourse['guest'], $bulkcourse['enrolla ble'], $bulkcourse['enrolstartdate'], $bulkcourse['enrolenddate'], $bulkcourse[' notifystudents'], $bulkcourse['expirynotify'], $bulkcourse['expirythreshold'], $ bulkcourse, $header, $validate, $bulkcourse['topics'], $bulkcourse['teachers_enr ol'])) { switch (fastcreatecourse_ex($coursetocat egory, $bulkcourse, $header, $validate)) { case 1: $n++; // Succeeded //for a successful course creati on, set to allow enrolments [added 6/3/2011] //adds record to enrol table, to allow manual enrolments in course. $result=mysql_query('SELECT MAX( `id`) FROM `'.$CFG->prefix.'course`'); $row = mysql_fetch_row($result) ; $id2 = $row[0]; // course id for this course $result = mysql_query('SELECT MA X(`id`) FROM `'.$CFG->prefix.'enrol`'); $row = mysql_fetch_row($result) ; $id1 = $row[0] +1; // id for new entry in enrol ta ble $ans= mysql_query('INSERT INTO `'.$CFG->prefix.'enrol` ( `id`,`enrol`,`courseid`,`roleid`) VALUES ('.$id1.',"manual",'.$id2.',5);');// 5 = student role if (!$ans) notify('ERROR in allowing enrolments', 'notif yproblem'); //end of insert enrol record break; case -3: notify('Could not create topic sections for course with ShortName '.$bulkcourse['shortname'],'notifyproblem'); $p++; break; case -4: notify('Could not enrol teachers for course with ShortNa me '.$bulkcourse['shortname'],'notifyproblem'); $p++; break; case -5:

notify('Could not find teacher r ole for course with ShortName '.$bulkcourse['shortname'],'notifyproblem'); $p++; break; case -6: notify('Could not enrol teacher for course due to course misconfiguration. Course ShortName: '.$bulkcourse['shor tname'],'notifyproblem'); $p++; break; case -7: notify('Could not find template for course with ShortName '.$bulkcourse['template'].' Course with ShortName '.$b ulkcourse['shortname'].' failed.','notifyproblem'); $p++; break; case -8: notify('SQL Template Error for t emplate course with ShortName '.$bulkcourse['template'].' Course with ShortName '.$bulkcourse['shortname'].' failed.','notifyproblem'); $p++; break; default: notify('An Error occured creating course with ShortName '.$bulkcourse['shortname'],'notifyproblem'); break; } } } else { // Skip course, already exists $s++; } $t++; } notify('Created '.$n.' course(s) out of '.$t,'notifysuccess'); if ($s > 0) notify ($s.' course(s) were skipped (duplicate ShortName)','notifysu ccess'); if ($p > 0) notify ($p.' course(s) could not be fully created, due to errors. Th ough they make work, it is advisable to delete them and try again.','notifyprobl em'); if (($e = $t - $n - $s - $p) > 0) notify ($e.' course(s) failed due to errors','notifyproblem'); if ($cat_e > 0) notify ($cat_e.' new category(ies) failed due to errors','notifyprob lem'); if ($cat_c > 0) { notify ($cat_c.' new category(ies) were created','notifysuccess'); notify ('You may wish to manually Re-Sort the categories','notifysuc cess'); // We don't automatically sort alphabetically, as the somewhat clunk y category sorting // method used by moodle could annoy the user

fix_course_sortorder(); // Re-sort courses notify('Re-Sorted courses','notifysuccess'); // We potentially did a lot to the Database: avoid tempting fate if (mysql_query('ANALYZE TABLE `'.$CFG->prefix.'course`') && mysql_query ('ANALYZE TABLE `'.$CFG->prefix.'course_categories`') && mysql_query('ANALYZE TA BLE `'.$CFG->prefix.'course_sections`') && mysql_query('ANALYZE TABLE `'.$CFG->p refix.'user_teachers`')) notify('ANALYZE Database Tables OK','notifysuccess'); else notify('Could not ANALYZE Database Tables (`'.$CFG->prefix.'course`, `'.$CFG->prefix.'course_categories`, `'.$CFG->prefix.'course_sections` and `'.$ CFG->prefix.'user_teachers`) - You should do this manually','notifyproblem'); if (mysql_query('OPTIMIZE TABLE `'.$CFG->prefix.'course`') && mysql_quer y('OPTIMIZE TABLE `'.$CFG->prefix.'course_categories`') && mysql_query('OPTIMIZE TABLE `'.$CFG->prefix.'course_sections`') && mysql_query('OPTIMIZE TABLE `'.$CF G->prefix.'user_teachers`')) notify('OPTIMIZE Database Tables OK','notifysuccess'); else notify('Could not OPTIMIZE Database Tables (`'.$CFG->prefix.'course` , `'.$CFG->prefix.'course_categories`, `'.$CFG->prefix.'course_sections` and `'. $CFG->prefix.'user_teachers`) - You should do this manually','notifyproblem'); $time_end = microtime_float(); notify('Total Execution Time: '.round(($time_end - $time_start),2).' s', 'notifysuccess'); echo '<hr />'; } else { // Print Help ?><center><p>Upload an <a href="http://www.rfc-editor.org/rfc/rfc4180.txt" t arget="_blank">RFC4180</a>-Compliant CSV file.<br />Valid fields for each course are:</p><table border="1" style="font-family:monospace;font-size:8pt;width:500p x;"> <tr><th>Field</th><th>Value</th></tr> <tr><td>category</td><td>[Forward]Slash-Delimited Category &quot;Path&quot; Stri ng (new categories are created as necessary) OR Integer Database Category ID</td ></tr> <tr><td>format</td><td>String('social','topics','weeks')</td></tr> <tr><td>fullname</td><td>String(254)</td></tr> <tr><td>groupmode</td><td>0=NOGROUPS,1=SEPARATEGROUPS,2=VISIBLEGROUPS</td></tr> <tr><td>groupmodeforce</td><td>0=FALSE,1=TRUE</td></tr> <tr><td>idnumber</td><td>String(100)</td></tr> <tr><td>lang</td><td>String(10)</td></tr> <tr><td>maxbytes</td><td>Integer(Site Max)</td></tr> <tr><td>newsitems</td><td>Integer(10)</td></tr> <tr><td>numsections</td><td>Integer(52)</td></tr> <tr><td>shortname</td><td>String(15)</td></tr> <tr><td>showgrades</td><td>0=FALSE,1=TRUE</td></tr> <tr><td>showreports</td><td>0=FALSE,1=TRUE</td></tr>

<tr><td>sortorder</td><td>Integer</td></tr> <tr><td>startdate</td><td>String Date Literal</td></tr> <tr><td>summary</td><td>Text</td></tr> <tr><td>theme</td><td>String(50)</td></tr> <tr><td>timecreated</td><td>String Date Literal</td></tr> <tr><td>timemodified</td><td>String Date Literal</td></tr> <tr><td>visible</td><td>0=FALSE,1=TRUE</td></tr> </table></center> <?php } /// Print the form $OUTPUT->heading('Upload Courses'); $maxuploadsize = get_max_upload_file_size(); echo '<center>'; echo '<form method="post" enctype="multipart/form-data" action="uploadcourse .php">'. $strchoose.':<input type="hidden" name="MAX_FILE_SIZE" value="'.$maxupl oadsize.'">'. '<input type="hidden" name="sesskey" value="'.$USER->sesskey.'">'. '<input type="file" name="coursefile" size="30">'. '<input type="submit" value="Upload">'. '</form></br>'; echo '</center>'; echo $OUTPUT->footer();

function my_file_get_contents($filename, $use_include_path = 0) { /// Returns the file as one big long string $data = ""; $file = @fopen($filename, "rb", $use_include_path); if ($file) { while (!feof($file)) { $data .= fread($file, 1024); } fclose($file); } return $data; } ?>