See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * Import outcomes from a file 19 * 20 * @package core_grades 21 * @copyright 2008 Moodle Pty Ltd (http://moodle.com) 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 require_once(__DIR__.'/../../../config.php'); 26 require_once($CFG->dirroot.'/lib/formslib.php'); 27 require_once($CFG->dirroot.'/grade/lib.php'); 28 require_once($CFG->libdir.'/gradelib.php'); 29 require_once ('import_outcomes_form.php'); 30 31 $courseid = optional_param('courseid', 0, PARAM_INT); 32 $action = optional_param('action', '', PARAM_ALPHA); 33 $scope = optional_param('scope', 'custom', PARAM_ALPHA); 34 35 $url = new moodle_url('/grade/edit/outcome/import.php', array('courseid' => $courseid)); 36 $PAGE->set_url($url); 37 $PAGE->set_pagelayout('admin'); 38 39 /// Make sure they can even access this course 40 if ($courseid) { 41 if (!$course = $DB->get_record('course', array('id' => $courseid))) { 42 throw new \moodle_exception('invalidcourseid'); 43 } 44 require_login($course); 45 $context = context_course::instance($course->id); 46 47 if (empty($CFG->enableoutcomes)) { 48 redirect('../../index.php?id='.$courseid); 49 } 50 navigation_node::override_active_url(new moodle_url('/grade/edit/outcome/course.php', ['id' => $courseid])); 51 $PAGE->navbar->add(get_string('manageoutcomes', 'grades'), 52 new moodle_url('/grade/edit/outcome/index.php', ['id' => $courseid])); 53 $PAGE->navbar->add(get_string('importoutcomes', 'grades'), 54 new moodle_url('/grade/edit/outcome/import.php', ['courseid' => $courseid])); 55 56 } else { 57 require_once $CFG->libdir.'/adminlib.php'; 58 admin_externalpage_setup('outcomes'); 59 $context = context_system::instance(); 60 } 61 62 require_capability('moodle/grade:manageoutcomes', $context); 63 64 $upload_form = new import_outcomes_form(); 65 66 if ($upload_form->is_cancelled()) { 67 redirect(new moodle_url('/grade/edit/outcome/index.php', ['id' => $courseid])); 68 die; 69 } 70 71 print_grade_page_head($courseid, 'outcome', 'import', get_string('importoutcomes', 'grades'), 72 false, false, false); 73 74 if (!$upload_form->get_data()) { // Display the import form. 75 $upload_form->display(); 76 echo $OUTPUT->footer(); 77 die; 78 } 79 80 $imported_file = $CFG->tempdir . '/outcomeimport/importedfile_'.time().'.csv'; 81 make_temp_directory('outcomeimport'); 82 83 // copying imported file 84 if (!$upload_form->save_file('userfile', $imported_file, true)) { 85 redirect('import.php'. ($courseid ? "?courseid=$courseid" : ''), get_string('importfilemissing', 'grades')); 86 } 87 88 /// which scope are we importing the outcomes in? 89 if (isset($courseid) && ($scope == 'custom')) { 90 // custom scale 91 $local_scope = true; 92 } elseif (($scope == 'global') && has_capability('moodle/grade:manage', context_system::instance())) { 93 // global scale 94 $local_scope = false; 95 } else { 96 // shouldn't happen .. user might be trying to access this script without the right permissions. 97 redirect('index.php', get_string('importerror', 'grades')); 98 } 99 100 // open the file, start importing data 101 if ($handle = fopen($imported_file, 'r')) { 102 $line = 0; // will keep track of current line, to give better error messages. 103 $file_headers = ''; 104 105 // $csv_data needs to have at least these columns, the value is the default position in the data file. 106 $headers = array('outcome_name' => 0, 'outcome_shortname' => 1, 'scale_name' => 3, 'scale_items' => 4); 107 $optional_headers = array('outcome_description'=>2, 'scale_description' => 5); 108 $imported_headers = array(); // will later be initialized with the values found in the file 109 110 $fatal_error = false; 111 $errormessage = ''; 112 113 // data should be separated by a ';'. *NOT* by a comma! TODO: version 2.0 114 // or whenever we can depend on PHP5, set the second parameter (8192) to 0 (unlimited line length) : the database can store over 128k per line. 115 while ( $csv_data = fgetcsv($handle, 8192, ';', '"')) { // if the line is over 8k, it won't work... 116 $line++; 117 118 // be tolerant on input, as fgetcsv returns "an array comprising a single null field" on blank lines 119 if ($csv_data == array(null)) { 120 continue; 121 } 122 123 // on first run, grab and analyse the header 124 if ($file_headers == '') { 125 126 $file_headers = array_flip($csv_data); // save the header line ... TODO: use the header line to let import work with columns in arbitrary order 127 128 $error = false; 129 foreach($headers as $key => $value) { 130 // sanity check #1: make sure the file contains all the mandatory headers 131 if (!array_key_exists($key, $file_headers)) { 132 $error = true; 133 break; 134 } 135 } 136 if ($error) { 137 $fatal_error = true; 138 $errormessage = get_string('importoutcomenofile', 'grades', $line); 139 break; 140 } 141 142 foreach(array_merge($headers, $optional_headers) as $header => $position) { 143 // match given columns to expected columns *into* $headers 144 $imported_headers[$header] = $file_headers[$header]; 145 } 146 147 continue; // we don't import headers 148 } 149 150 // sanity check #2: every line must have the same number of columns as there are 151 // headers. If not, processing stops. 152 if ( count($csv_data) != count($file_headers) ) { 153 $fatal_error = true; 154 $errormessage = get_string('importoutcomenofile', 'grades', $line); 155 break; 156 } 157 158 // sanity check #3: all required fields must be present on the current line. 159 foreach ($headers as $header => $position) { 160 if ($csv_data[$imported_headers[$header]] == '') { 161 $fatal_error = true; 162 $errormessage = get_string('importoutcomenofile', 'grades', $line); 163 break; 164 } 165 } 166 167 // MDL-17273 errors in csv are not preventing import from happening. We break from the while loop here 168 if ($fatal_error) { 169 break; 170 } 171 $params = array($csv_data[$imported_headers['outcome_shortname']]); 172 $wheresql = 'shortname = ? '; 173 174 if ($local_scope) { 175 $params[] = $courseid; 176 $wheresql .= ' AND courseid = ?'; 177 } else { 178 $wheresql .= ' AND courseid IS NULL'; 179 } 180 181 $outcome = $DB->get_records_select('grade_outcomes', $wheresql, $params); 182 183 if ($outcome) { 184 // already exists, print a message and skip. 185 echo $OUTPUT->notification(get_string('importskippedoutcome', 'grades', 186 $csv_data[$imported_headers['outcome_shortname']]), 'info', false); 187 continue; 188 } 189 190 // new outcome will be added, search for compatible existing scale... 191 $params = array($csv_data[$imported_headers['scale_name']], $csv_data[$imported_headers['scale_items']], $courseid); 192 $wheresql = 'name = ? AND scale = ? AND (courseid = ? OR courseid = 0)'; 193 $scale = $DB->get_records_select('scale', $wheresql, $params); 194 195 if ($scale) { 196 // already exists in the right scope: use it. 197 $scale_id = key($scale); 198 } else { 199 if (!has_capability('moodle/course:managescales', $context)) { 200 echo $OUTPUT->notification(get_string('importskippedoutcome', 'grades', 201 $csv_data[$imported_headers['outcome_shortname']]), 'warning', false); 202 continue; 203 } else { 204 // scale doesn't exists : create it. 205 $scale_data = array('name' => $csv_data[$imported_headers['scale_name']], 206 'scale' => $csv_data[$imported_headers['scale_items']], 207 'description' => $csv_data[$imported_headers['scale_description']], 208 'userid' => $USER->id); 209 210 if ($local_scope) { 211 $scale_data['courseid'] = $courseid; 212 } else { 213 $scale_data['courseid'] = 0; // 'global' : scale use '0', outcomes use null 214 } 215 $scale = new grade_scale($scale_data); 216 $scale_id = $scale->insert(); 217 } 218 } 219 220 // add outcome 221 $outcome_data = array('shortname' => $csv_data[$imported_headers['outcome_shortname']], 222 'fullname' => $csv_data[$imported_headers['outcome_name']], 223 'scaleid' => $scale_id, 224 'description' => $csv_data[$imported_headers['outcome_description']], 225 'usermodified' => $USER->id); 226 227 if ($local_scope) { 228 $outcome_data['courseid'] = $courseid; 229 } else { 230 $outcome_data['courseid'] = null; // 'global' : scale use '0', outcomes use null 231 } 232 $outcome = new grade_outcome($outcome_data); 233 $outcome_id = $outcome->insert(); 234 235 $outcome_success_strings = new StdClass(); 236 $outcome_success_strings->name = $outcome_data['fullname']; 237 $outcome_success_strings->id = $outcome_id; 238 echo $OUTPUT->notification(get_string('importoutcomesuccess', 'grades', $outcome_success_strings), 239 'success', false); 240 } 241 242 if ($fatal_error) { 243 echo $OUTPUT->notification($errormessage, 'error', false); 244 echo $OUTPUT->single_button(new moodle_url('/grade/edit/outcome/import.php', ['courseid' => $courseid]), 245 get_string('back'), 'get'); 246 } else { 247 echo $OUTPUT->single_button(new moodle_url('/grade/edit/outcome/index.php', ['id' => $courseid]), 248 get_string('continue'), 'get'); 249 } 250 } else { 251 echo $OUTPUT->box(get_string('importoutcomenofile', 'grades', 0)); 252 } 253 254 // finish 255 fclose($handle); 256 // delete temp file 257 unlink($imported_file); 258 259 echo $OUTPUT->footer();
title
Description
Body
title
Description
Body
title
Description
Body
title
Body