|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 00003 // This file is part of Moodle - http://moodle.org/ 00004 // 00005 // Moodle is free software: you can redistribute it and/or modify 00006 // it under the terms of the GNU General Public License as published by 00007 // the Free Software Foundation, either version 3 of the License, or 00008 // (at your option) any later version. 00009 // 00010 // Moodle is distributed in the hope that it will be useful, 00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 // GNU General Public License for more details. 00014 // 00015 // You should have received a copy of the GNU General Public License 00016 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 00017 00018 require_once '../../../config.php'; 00019 require_once $CFG->libdir.'/gradelib.php'; 00020 require_once $CFG->dirroot.'/grade/lib.php'; 00021 require_once '../grade_import_form.php'; 00022 require_once '../lib.php'; 00023 00024 $id = required_param('id', PARAM_INT); // course id 00025 $separator = optional_param('separator', '', PARAM_ALPHA); 00026 $verbosescales = optional_param('verbosescales', 1, PARAM_BOOL); 00027 00028 $url = new moodle_url('/grade/import/csv/index.php', array('id'=>$id)); 00029 if ($separator !== '') { 00030 $url->param('separator', $separator); 00031 } 00032 if ($verbosescales !== 1) { 00033 $url->param('verbosescales', $verbosescales); 00034 } 00035 $PAGE->set_url($url); 00036 00037 define('GRADE_CSV_LINE_LENGTH', 4096); 00038 00039 if (!$course = $DB->get_record('course', array('id'=>$id))) { 00040 print_error('nocourseid'); 00041 } 00042 00043 require_login($course); 00044 $context = get_context_instance(CONTEXT_COURSE, $id); 00045 require_capability('moodle/grade:import', $context); 00046 require_capability('gradeimport/csv:view', $context); 00047 00048 $separatemode = (groups_get_course_groupmode($COURSE) == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)); 00049 $currentgroup = groups_get_course_group($course); 00050 00051 // sort out delimiter 00052 if (isset($CFG->CSV_DELIMITER)) { 00053 $csv_delimiter = $CFG->CSV_DELIMITER; 00054 00055 if (isset($CFG->CSV_ENCODE)) { 00056 $csv_encode = '/\&\#' . $CFG->CSV_ENCODE . '/'; 00057 } 00058 } else if ($separator == 'tab') { 00059 $csv_delimiter = "\t"; 00060 $csv_encode = ""; 00061 } else { 00062 $csv_delimiter = ","; 00063 $csv_encode = '/\&\#44/'; 00064 } 00065 00066 print_grade_page_head($course->id, 'import', 'csv', get_string('importcsv', 'grades')); 00067 00068 // set up import form 00069 $mform = new grade_import_form(null, array('includeseparator'=>!isset($CFG->CSV_DELIMITER), 'verbosescales'=>true)); 00070 00071 // set up grade import mapping form 00072 $header = ''; 00073 $gradeitems = array(); 00074 if ($id) { 00075 if ($grade_items = grade_item::fetch_all(array('courseid'=>$id))) { 00076 foreach ($grade_items as $grade_item) { 00077 // skip course type and category type 00078 if ($grade_item->itemtype == 'course' || $grade_item->itemtype == 'category') { 00079 continue; 00080 } 00081 00082 $displaystring = null; 00083 if (!empty($grade_item->itemmodule)) { 00084 $displaystring = get_string('modulename', $grade_item->itemmodule).': '.$grade_item->get_name(); 00085 } else { 00086 $displaystring = $grade_item->get_name(); 00087 } 00088 $gradeitems[$grade_item->id] = $displaystring; 00089 } 00090 } 00091 } 00092 00093 if ($importcode = optional_param('importcode', '', PARAM_FILE)) { 00094 $filename = $CFG->tempdir.'/gradeimport/cvs/'.$USER->id.'/'.$importcode; 00095 $fp = fopen($filename, "r"); 00096 $headers = fgets($fp, GRADE_CSV_LINE_LENGTH); 00097 $header = explode($csv_delimiter, $headers); 00098 fclose($fp); 00099 } 00100 00101 $mform2 = new grade_import_mapping_form(null, array('gradeitems'=>$gradeitems, 'header'=>$header)); 00102 00103 // if import form is submitted 00104 if ($formdata = $mform->get_data()) { 00105 00106 // Large files are likely to take their time and memory. Let PHP know 00107 // that we'll take longer, and that the process should be recycled soon 00108 // to free up memory. 00109 @set_time_limit(0); 00110 raise_memory_limit(MEMORY_EXTRA); 00111 00112 // use current (non-conflicting) time stamp 00113 $importcode = get_new_importcode(); 00114 $filename = make_temp_directory('gradeimport/cvs/'.$USER->id); 00115 $filename = $filename.'/'.$importcode; 00116 00117 $text = $mform->get_file_content('userfile'); 00118 // trim utf-8 bom 00119 $textlib = textlib_get_instance(); 00121 $text = $textlib->convert($text, $formdata->encoding); 00122 $text = $textlib->trim_utf8_bom($text); 00123 // Fix mac/dos newlines 00124 $text = preg_replace('!\r\n?!',"\n",$text); 00125 $fp = fopen($filename, "w"); 00126 fwrite($fp,$text); 00127 fclose($fp); 00128 00129 $fp = fopen($filename, "r"); 00130 00131 // --- get header (field names) --- 00132 $header = explode($csv_delimiter, fgets($fp, GRADE_CSV_LINE_LENGTH)); 00133 00134 // print some preview 00135 $numlines = 0; // 0 preview lines displayed 00136 00137 echo $OUTPUT->heading(get_string('importpreview', 'grades')); 00138 echo '<table>'; 00139 echo '<tr>'; 00140 foreach ($header as $h) { 00141 $h = clean_param($h, PARAM_RAW); 00142 echo '<th>'.$h.'</th>'; 00143 } 00144 echo '</tr>'; 00145 while (!feof ($fp) && $numlines <= $formdata->previewrows) { 00146 $lines = explode($csv_delimiter, fgets($fp, GRADE_CSV_LINE_LENGTH)); 00147 echo '<tr>'; 00148 foreach ($lines as $line) { 00149 echo '<td>'.$line.'</td>'; 00150 } 00151 $numlines ++; 00152 echo '</tr>'; 00153 } 00154 echo '</table>'; 00155 00156 // display the mapping form with header info processed 00157 $mform2 = new grade_import_mapping_form(null, array('gradeitems'=>$gradeitems, 'header'=>$header)); 00158 $mform2->set_data(array('importcode'=>$importcode, 'id'=>$id, 'verbosescales'=>$verbosescales, 'separator'=>$separator)); 00159 $mform2->display(); 00160 00161 //} else if (($formdata = data_submitted()) && !empty($formdata->map)) { 00162 00163 // else if grade import mapping form is submitted 00164 } else if ($formdata = $mform2->get_data()) { 00165 00166 $importcode = clean_param($formdata->importcode, PARAM_FILE); 00167 $filename = $CFG->tempdir.'/gradeimport/cvs/'.$USER->id.'/'.$importcode; 00168 00169 if (!file_exists($filename)) { 00170 print_error('cannotuploadfile'); 00171 } 00172 00173 if ($fp = fopen($filename, "r")) { 00174 // --- get header (field names) --- 00175 $header = explode($csv_delimiter, clean_param(fgets($fp,GRADE_CSV_LINE_LENGTH), PARAM_RAW)); 00176 00177 foreach ($header as $i => $h) { 00178 $h = trim($h); $header[$i] = $h; // remove whitespace 00179 } 00180 } else { 00181 print_error('cannotopenfile'); 00182 } 00183 00184 $map = array(); 00185 // loops mapping_0, mapping_1 .. mapping_n and construct $map array 00186 foreach ($header as $i => $head) { 00187 if (isset($formdata->{'mapping_'.$i})) { 00188 $map[$i] = $formdata->{'mapping_'.$i}; 00189 } 00190 } 00191 00192 // if mapping information is supplied 00193 $map[clean_param($formdata->mapfrom, PARAM_RAW)] = clean_param($formdata->mapto, PARAM_RAW); 00194 00195 // check for mapto collisions 00196 $maperrors = array(); 00197 foreach ($map as $i => $j) { 00198 if ($j == 0) { 00199 // you can have multiple ignores 00200 continue; 00201 } else { 00202 if (!isset($maperrors[$j])) { 00203 $maperrors[$j] = true; 00204 } else { 00205 // collision 00206 fclose($fp); 00207 unlink($filename); // needs to be uploaded again, sorry 00208 print_error('cannotmapfield', '', '', $j); 00209 } 00210 } 00211 } 00212 00213 // Large files are likely to take their time and memory. Let PHP know 00214 // that we'll take longer, and that the process should be recycled soon 00215 // to free up memory. 00216 @set_time_limit(0); 00217 raise_memory_limit(MEMORY_EXTRA); 00218 00219 // we only operate if file is readable 00220 if ($fp = fopen($filename, "r")) { 00221 00222 // read the first line makes sure this doesn't get read again 00223 $header = explode($csv_delimiter, fgets($fp, GRADE_CSV_LINE_LENGTH)); 00224 00225 $newgradeitems = array(); // temporary array to keep track of what new headers are processed 00226 $status = true; 00227 00228 while (!feof ($fp)) { 00229 // add something 00230 $line = explode($csv_delimiter, fgets($fp, GRADE_CSV_LINE_LENGTH)); 00231 00232 if(count($line) <= 1){ 00233 // there is no data on this line, move on 00234 continue; 00235 } 00236 00237 // array to hold all grades to be inserted 00238 $newgrades = array(); 00239 // array to hold all feedback 00240 $newfeedbacks = array(); 00241 // each line is a student record 00242 foreach ($line as $key => $value) { 00243 //decode encoded commas 00244 $value = clean_param($value, PARAM_RAW); 00245 $value = trim($value); 00246 if (!empty($csv_encode)) { 00247 $value = preg_replace($csv_encode, $csv_delimiter, $value); 00248 } 00249 00250 /* 00251 * the options are 00252 * 1) userid, useridnumber, usermail, username - used to identify user row 00253 * 2) new - new grade item 00254 * 3) id - id of the old grade item to map onto 00255 * 3) feedback_id - feedback for grade item id 00256 */ 00257 00258 $t = explode("_", $map[$key]); 00259 $t0 = $t[0]; 00260 if (isset($t[1])) { 00261 $t1 = (int)$t[1]; 00262 } else { 00263 $t1 = ''; 00264 } 00265 00266 switch ($t0) { 00267 case 'userid': // 00268 if (!$user = $DB->get_record('user', array('id' => $value))) { 00269 // user not found, abort whole import 00270 import_cleanup($importcode); 00271 echo $OUTPUT->notification("user mapping error, could not find user with id \"$value\""); 00272 $status = false; 00273 break 3; 00274 } 00275 $studentid = $value; 00276 break; 00277 case 'useridnumber': 00278 if (!$user = $DB->get_record('user', array('idnumber' => $value))) { 00279 // user not found, abort whole import 00280 import_cleanup($importcode); 00281 echo $OUTPUT->notification("user mapping error, could not find user with idnumber \"$value\""); 00282 $status = false; 00283 break 3; 00284 } 00285 $studentid = $user->id; 00286 break; 00287 case 'useremail': 00288 if (!$user = $DB->get_record('user', array('email' => $value))) { 00289 import_cleanup($importcode); 00290 echo $OUTPUT->notification("user mapping error, could not find user with email address \"$value\""); 00291 $status = false; 00292 break 3; 00293 } 00294 $studentid = $user->id; 00295 break; 00296 case 'username': 00297 if (!$user = $DB->get_record('user', array('username' => $value))) { 00298 import_cleanup($importcode); 00299 echo $OUTPUT->notification("user mapping error, could not find user with username \"$value\""); 00300 $status = false; 00301 break 3; 00302 } 00303 $studentid = $user->id; 00304 break; 00305 case 'new': 00306 // first check if header is already in temp database 00307 00308 if (empty($newgradeitems[$key])) { 00309 00310 $newgradeitem = new stdClass(); 00311 $newgradeitem->itemname = $header[$key]; 00312 $newgradeitem->importcode = $importcode; 00313 $newgradeitem->importer = $USER->id; 00314 00315 // insert into new grade item buffer 00316 $newgradeitems[$key] = $DB->insert_record('grade_import_newitem', $newgradeitem); 00317 } 00318 $newgrade = new stdClass(); 00319 $newgrade->newgradeitem = $newgradeitems[$key]; 00320 00321 // if the user has a grade for this grade item 00322 if (trim($value) != '-') { 00323 // instead of omitting the grade we could insert one with finalgrade set to 0 00324 // we do not have access to grade item min grade 00325 $newgrade->finalgrade = $value; 00326 $newgrades[] = $newgrade; 00327 } 00328 break; 00329 case 'feedback': 00330 if ($t1) { 00331 // case of an id, only maps id of a grade_item 00332 // this was idnumber 00333 if (!$gradeitem = new grade_item(array('id'=>$t1, 'courseid'=>$course->id))) { 00334 // supplied bad mapping, should not be possible since user 00335 // had to pick mapping 00336 $status = false; 00337 import_cleanup($importcode); 00338 echo $OUTPUT->notification(get_string('importfailed', 'grades')); 00339 break 3; 00340 } 00341 00342 // t1 is the id of the grade item 00343 $feedback = new stdClass(); 00344 $feedback->itemid = $t1; 00345 $feedback->feedback = $value; 00346 $newfeedbacks[] = $feedback; 00347 } 00348 break; 00349 default: 00350 // existing grade items 00351 if (!empty($map[$key])) { 00352 // case of an id, only maps id of a grade_item 00353 // this was idnumber 00354 if (!$gradeitem = new grade_item(array('id'=>$map[$key], 'courseid'=>$course->id))) { 00355 // supplied bad mapping, should not be possible since user 00356 // had to pick mapping 00357 $status = false; 00358 import_cleanup($importcode); 00359 echo $OUTPUT->notification(get_string('importfailed', 'grades')); 00360 break 3; 00361 } 00362 00363 // check if grade item is locked if so, abort 00364 if ($gradeitem->is_locked()) { 00365 $status = false; 00366 import_cleanup($importcode); 00367 echo $OUTPUT->notification(get_string('gradeitemlocked', 'grades')); 00368 break 3; 00369 } 00370 00371 $newgrade = new stdClass(); 00372 $newgrade->itemid = $gradeitem->id; 00373 if ($gradeitem->gradetype == GRADE_TYPE_SCALE and $verbosescales) { 00374 if ($value === '' or $value == '-') { 00375 $value = null; // no grade 00376 } else { 00377 $scale = $gradeitem->load_scale(); 00378 $scales = explode(',', $scale->scale); 00379 $scales = array_map('trim', $scales); //hack - trim whitespace around scale options 00380 array_unshift($scales, '-'); // scales start at key 1 00381 $key = array_search($value, $scales); 00382 if ($key === false) { 00383 echo "<br/>t0 is $t0"; 00384 echo "<br/>grade is $value"; 00385 $status = false; 00386 import_cleanup($importcode); 00387 echo $OUTPUT->notification(get_string('badgrade', 'grades')); 00388 break 3; 00389 } 00390 $value = $key; 00391 } 00392 $newgrade->finalgrade = $value; 00393 } else { 00394 if ($value === '' or $value == '-') { 00395 $value = null; // no grade 00396 00397 } else if (!is_numeric($value)) { 00398 // non numeric grade value supplied, possibly mapped wrong column 00399 echo "<br/>t0 is $t0"; 00400 echo "<br/>grade is $value"; 00401 $status = false; 00402 import_cleanup($importcode); 00403 echo $OUTPUT->notification(get_string('badgrade', 'grades')); 00404 break 3; 00405 } 00406 $newgrade->finalgrade = $value; 00407 } 00408 $newgrades[] = $newgrade; 00409 } // otherwise, we ignore this column altogether 00410 // because user has chosen to ignore them (e.g. institution, address etc) 00411 break; 00412 } 00413 } 00414 00415 // no user mapping supplied at all, or user mapping failed 00416 if (empty($studentid) || !is_numeric($studentid)) { 00417 // user not found, abort whole import 00418 $status = false; 00419 import_cleanup($importcode); 00420 echo $OUTPUT->notification('user mapping error, could not find user!'); 00421 break; 00422 } 00423 00424 if ($separatemode and !groups_is_member($currentgroup, $studentid)) { 00425 // not allowed to import into this group, abort 00426 $status = false; 00427 import_cleanup($importcode); 00428 echo $OUTPUT->notification('user not member of current group, can not update!'); 00429 break; 00430 } 00431 00432 // insert results of this students into buffer 00433 if ($status and !empty($newgrades)) { 00434 00435 foreach ($newgrades as $newgrade) { 00436 00437 // check if grade_grade is locked and if so, abort 00438 if (!empty($newgrade->itemid) and $grade_grade = new grade_grade(array('itemid'=>$newgrade->itemid, 'userid'=>$studentid))) { 00439 if ($grade_grade->is_locked()) { 00440 // individual grade locked 00441 $status = false; 00442 import_cleanup($importcode); 00443 echo $OUTPUT->notification(get_string('gradelocked', 'grades')); 00444 break 2; 00445 } 00446 } 00447 00448 $newgrade->importcode = $importcode; 00449 $newgrade->userid = $studentid; 00450 $newgrade->importer = $USER->id; 00451 $DB->insert_record('grade_import_values', $newgrade); 00452 } 00453 } 00454 00455 // updating/inserting all comments here 00456 if ($status and !empty($newfeedbacks)) { 00457 foreach ($newfeedbacks as $newfeedback) { 00458 $sql = "SELECT * 00459 FROM {grade_import_values} 00460 WHERE importcode=? AND userid=? AND itemid=? AND importer=?"; 00461 if ($feedback = $DB->get_record_sql($sql, array($importcode, $studentid, $newfeedback->itemid, $USER->id))) { 00462 $newfeedback->id = $feedback->id; 00463 $DB->update_record('grade_import_values', $newfeedback); 00464 00465 } else { 00466 // the grade item for this is not updated 00467 $newfeedback->importcode = $importcode; 00468 $newfeedback->userid = $studentid; 00469 $newfeedback->importer = $USER->id; 00470 $DB->insert_record('grade_import_values', $newfeedback); 00471 } 00472 } 00473 } 00474 } 00475 00477 if ($status) { 00478 grade_import_commit($course->id, $importcode); 00479 } 00480 // temporary file can go now 00481 fclose($fp); 00482 unlink($filename); 00483 } else { 00484 print_error('cannotreadfile'); 00485 } 00486 00487 } else { 00488 groups_print_course_menu($course, 'index.php?id='.$id); 00489 echo '<div class="clearer"></div>'; 00490 00491 // display the standard upload file form 00492 $mform->display(); 00493 } 00494 00495 echo $OUTPUT->footer(); 00496